Finished chapter 11
authorNeil Smith <neil.git@njae.me.uk>
Wed, 11 Mar 2009 12:24:28 +0000 (12:24 +0000)
committerNeil Smith <neil.git@njae.me.uk>
Wed, 11 Mar 2009 12:24:28 +0000 (12:24 +0000)
24 files changed:
app/controllers/admin_controller.rb [new file with mode: 0644]
app/controllers/application.rb
app/controllers/store_controller.rb
app/controllers/users_controller.rb [new file with mode: 0644]
app/helpers/admin_helper.rb [new file with mode: 0644]
app/helpers/users_helper.rb [new file with mode: 0644]
app/models/user.rb [new file with mode: 0644]
app/views/admin/index.html.erb [new file with mode: 0644]
app/views/admin/login.html.erb [new file with mode: 0644]
app/views/admin/logout.html.erb [new file with mode: 0644]
app/views/layouts/orders.html.erb [deleted file]
app/views/layouts/products.html.erb [deleted file]
app/views/layouts/store.html.erb
app/views/users/edit.html.erb [new file with mode: 0644]
app/views/users/index.html.erb [new file with mode: 0644]
app/views/users/new.html.erb [new file with mode: 0644]
app/views/users/show.html.erb [new file with mode: 0644]
config/routes.rb
db/migrate/20090311105014_create_users.rb [new file with mode: 0644]
db/schema.rb
test/fixtures/users.yml [new file with mode: 0644]
test/functional/admin_controller_test.rb [new file with mode: 0644]
test/functional/users_controller_test.rb [new file with mode: 0644]
test/unit/user_test.rb [new file with mode: 0644]

diff --git a/app/controllers/admin_controller.rb b/app/controllers/admin_controller.rb
new file mode 100644 (file)
index 0000000..ade35e4
--- /dev/null
@@ -0,0 +1,27 @@
+class AdminController < ApplicationController
+  def login
+    session[:user_id] = nil
+    if request.post?
+      user = User.authenticate(params[:name], params[:password])
+      if user
+        session[:user_id] = user.id
+        uri = session[:original_uri]
+        session[:original_uri] = nil
+        redirect_to uri || {:action => 'index'}
+      else
+        flash.now[:notice] = "Invalid user/password combination"
+      end
+    end
+  end
+
+  def logout
+    session[:user_id] = nil
+    flash[:notice] = "Logged out"
+    redirect_to(:action => "login")
+  end
+
+  def index
+    @total_orders = Order.count
+  end
+
+end
index fea87e2d210d5e5dfe122bfcc526fb008889ab8c..fe5e10108c6b113c81ebd84c398559c229a91534 100644 (file)
@@ -2,6 +2,8 @@
 # Likewise, all the methods added will be available for all controllers.
 
 class ApplicationController < ActionController::Base
+  layout "store"
+  before_filter :authorize, :except => :login
   helper :all # include all helpers, all the time
 
   # See ActionController::RequestForgeryProtection for details
@@ -12,4 +14,13 @@ class ApplicationController < ActionController::Base
   # Uncomment this to filter the contents of submitted sensitive data parameters
   # from your application log (in this case, all fields with names like "password"). 
   # filter_parameter_logging :password
+
+  protected
+  def authorize
+    unless User.find_by_id(session[:user_id])
+      session[:original_uri] = request.request_uri
+      flash[:notice] = "Please log in"
+      redirect_to :controller => 'admin', 'action' => 'login'
+    end
+  end
 end
index 68939e6b09f31a82539e7ae7335eb80ce4466919..4c13d34155c6624dbf1efbd0f0c465de7860986e 100644 (file)
@@ -1,12 +1,14 @@
 class StoreController < ApplicationController
+  before_filter :find_cart, :except => :empty_cart
+
   def index
     @products = Product.find_products_for_sale
-    @cart = find_cart
+#    @cart = find_cart
   end
 
   def add_to_cart
     product = Product.find(params[:id])
-    @cart = find_cart
+#    @cart = find_cart
     @current_item = @cart.add_product(product)
       respond_to do |format|
         format.js if request.xhr?
@@ -23,7 +25,7 @@ class StoreController < ApplicationController
   end
 
   def checkout
-    @cart = find_cart
+#    @cart = find_cart
     if @cart.items.empty?
       redirect_to_index("Your cart is empty" )
     else
@@ -37,7 +39,7 @@ class StoreController < ApplicationController
   end
 
   def save_order
-    @cart = find_cart
+#    @cart = find_cart
     @order = Order.new(params[:order])
     # @order.add_line_items_from_cart(@cart)
     @cart.items.each do |item|
@@ -57,10 +59,16 @@ class StoreController < ApplicationController
   end
   
 
+protected
+
+  # No authorization needed for the store
+  def authorize
+  end
+
  private
  
   def find_cart
-    session[:cart] ||= Cart.new
+    @cart = (session[:cart] ||= Cart.new)
   end
 
   def redirect_to_index(msg = nil)
diff --git a/app/controllers/users_controller.rb b/app/controllers/users_controller.rb
new file mode 100644 (file)
index 0000000..9587740
--- /dev/null
@@ -0,0 +1,90 @@
+class UsersController < ApplicationController
+  # GET /users
+  # GET /users.xml
+  def index
+    @users = User.find(:all, :order => :name)
+
+    respond_to do |format|
+      format.html # index.html.erb
+      format.xml  { render :xml => @users }
+    end
+  end
+
+  # GET /users/1
+  # GET /users/1.xml
+  def show
+    @user = User.find(params[:id])
+
+    respond_to do |format|
+      format.html # show.html.erb
+      format.xml  { render :xml => @user }
+    end
+  end
+
+  # GET /users/new
+  # GET /users/new.xml
+  def new
+    @user = User.new
+
+    respond_to do |format|
+      format.html # new.html.erb
+      format.xml  { render :xml => @user }
+    end
+  end
+
+  # GET /users/1/edit
+  def edit
+    @user = User.find(params[:id])
+  end
+
+  # POST /users
+  # POST /users.xml
+  def create
+    @user = User.new(params[:user])
+
+    respond_to do |format|
+      if @user.save
+        flash[:notice] = "User #{@user.name} was successfully created."
+        format.html { redirect_to(:action => 'index') }
+        format.xml  { render :xml => @user, :status => :created, :location => @user }
+      else
+        format.html { render :action => "new" }
+        format.xml  { render :xml => @user.errors, :status => :unprocessable_entity }
+      end
+    end
+  end
+
+  # PUT /users/1
+  # PUT /users/1.xml
+  def update
+    @user = User.find(params[:id])
+
+    respond_to do |format|
+      if @user.update_attributes(params[:user])
+        flash[:notice] = "User #{@user.name} was successfully updated."
+        format.html { redirect_to(:action => 'index') }
+        format.xml  { head :ok }
+      else
+        format.html { render :action => "edit" }
+        format.xml  { render :xml => @user.errors, :status => :unprocessable_entity }
+      end
+    end
+  end
+
+  # DELETE /users/1
+  # DELETE /users/1.xml
+  def destroy
+    @user = User.find(params[:id])
+    begin
+      flash[:notice] = "User #{@user.name} deleted"
+      @user.destroy
+    rescue Exception => e
+      flash[:notice] = e.message
+    end
+
+    respond_to do |format|
+      format.html { redirect_to(users_url) }
+      format.xml  { head :ok }
+    end
+  end
+end
diff --git a/app/helpers/admin_helper.rb b/app/helpers/admin_helper.rb
new file mode 100644 (file)
index 0000000..d5c6d35
--- /dev/null
@@ -0,0 +1,2 @@
+module AdminHelper
+end
diff --git a/app/helpers/users_helper.rb b/app/helpers/users_helper.rb
new file mode 100644 (file)
index 0000000..2310a24
--- /dev/null
@@ -0,0 +1,2 @@
+module UsersHelper
+end
diff --git a/app/models/user.rb b/app/models/user.rb
new file mode 100644 (file)
index 0000000..c58f344
--- /dev/null
@@ -0,0 +1,55 @@
+require 'digest/sha1'
+
+class User < ActiveRecord::Base
+  validates_presence_of :name
+  validates_uniqueness_of :name
+  
+  attr_accessor :password_confirmation
+  validates_confirmation_of :password
+  
+  validate :password_non_blank
+
+  def password
+    @password
+  end
+
+  def password=(passwd)
+    @password = passwd
+    return if passwd.blank?
+    create_new_salt
+    self.hashed_password = User.encrypted_password(self.password, self.salt)
+  end
+
+  def self.authenticate(name, password)
+    user = self.find_by_name(name)
+    if user
+      expected_password = encrypted_password(password, user.salt)
+      if user.hashed_password != expected_password
+        user = nil
+      end
+    end
+    user
+  end
+
+  def after_destroy
+    if User.count.zero?
+      raise "Can't delete last user"
+    end
+  end
+
+private
+
+  def password_non_blank
+    errors.add(:password, "Missing password" ) if hashed_password.blank?
+  end
+
+  def self.encrypted_password(password, salt)
+    string_to_hash = password + 'wibble' + salt
+    Digest::SHA1.hexdigest string_to_hash
+  end
+
+  def create_new_salt
+    self.salt = self.object_id.to_s + rand.to_s
+  end
+
+end
diff --git a/app/views/admin/index.html.erb b/app/views/admin/index.html.erb
new file mode 100644 (file)
index 0000000..ec658d4
--- /dev/null
@@ -0,0 +1,4 @@
+
+<h1>Welcome, <%= User.find(session[:user_id]).name  %></h1>
+It's <%= Time.now %>.
+We have <%= pluralize(@total_orders, "order" ) %>.
\ No newline at end of file
diff --git a/app/views/admin/login.html.erb b/app/views/admin/login.html.erb
new file mode 100644 (file)
index 0000000..61c40a1
--- /dev/null
@@ -0,0 +1,19 @@
+
+<div class="depot-form">
+  <% form_tag do %>
+    <fieldset>
+      <legend>Please Log In</legend>
+      <div>
+        <label for="name">Name:</label>
+        <%= text_field_tag :name, params[:name] %>
+      </div>
+      <div>
+        <label for="password">Password:</label>
+        <%= password_field_tag :password, params[:password] %>
+      </div>
+      <div>
+        <%= submit_tag "Login" %>
+      </div>
+    </fieldset>
+  <% end %>
+</div>
\ No newline at end of file
diff --git a/app/views/admin/logout.html.erb b/app/views/admin/logout.html.erb
new file mode 100644 (file)
index 0000000..f3e955c
--- /dev/null
@@ -0,0 +1,2 @@
+<h1>Admin#logout</h1>
+<p>Find me in app/views/admin/logout.html.erb</p>
diff --git a/app/views/layouts/orders.html.erb b/app/views/layouts/orders.html.erb
deleted file mode 100644 (file)
index ee970df..0000000
+++ /dev/null
@@ -1,17 +0,0 @@
-<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN"
-       "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
-
-<html xmlns="http://www.w3.org/1999/xhtml" xml:lang="en" lang="en">
-<head>
-  <meta http-equiv="content-type" content="text/html;charset=UTF-8" />
-  <title>Orders: <%= controller.action_name %></title>
-  <%= stylesheet_link_tag 'scaffold' %>
-</head>
-<body>
-
-<p style="color: green"><%= flash[:notice] %></p>
-
-<%= yield  %>
-
-</body>
-</html>
diff --git a/app/views/layouts/products.html.erb b/app/views/layouts/products.html.erb
deleted file mode 100644 (file)
index 07fed7f..0000000
+++ /dev/null
@@ -1,17 +0,0 @@
-<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN"
-       "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
-
-<html xmlns="http://www.w3.org/1999/xhtml" xml:lang="en" lang="en">
-<head>
-  <meta http-equiv="content-type" content="text/html;charset=UTF-8" />
-  <title>Products: <%= controller.action_name %></title>
-  <%= stylesheet_link_tag 'scaffold', 'depot' %>
-</head>
-<body>
-
-<p style="color: green"><%= flash[:notice] %></p>
-
-<%= yield  %>
-
-</body>
-</html>
index b1f928e99aa5dd06fc4e613f969626f74767372d..e4845d76207dea4ff6b78016578a527a2bbefaf8 100644 (file)
     </div>
     <div id="columns">
       <div id="side">
-        <% hidden_div_if(@cart.items.empty?, :id => "cart") do %>
-            <%= render(:partial => "cart" , :object => @cart) %>
-        <% end %>
+        <% if @cart %>
+          <% hidden_div_if(@cart.items.empty?, :id => "cart") do %>
+              <%= render(:partial => "cart" , :object => @cart) %>
+          <% end %>
+        <% end  %>
         <a href="/">Home</a><br />
         <a href="http://www..../faq">Questions</a><br />
         <a href="http://www..../news">News</a><br />
         <a href="http://www..../contact">Contact</a><br />
+
+        <% if session[:user_id] %>
+          <br />
+          <%= link_to 'Orders',   :controller => 'orders' %><br />
+          <%= link_to 'Products', :controller => 'products' %><br />
+          <%= link_to 'Users',    :controller => 'users' %><br />
+          <br />
+          <%= link_to 'Logout', :controller => 'admin', :action => 'logout' %>
+        <% end %>
+
       </div>
       <div id="main">
         <% if flash[:notice] -%>
diff --git a/app/views/users/edit.html.erb b/app/views/users/edit.html.erb
new file mode 100644 (file)
index 0000000..52e9ee2
--- /dev/null
@@ -0,0 +1,31 @@
+<div class="depot-form">
+
+  <% form_for(@user) do |f| %>
+    <%= f.error_messages %>
+
+    <fieldset>
+      <legend>Update user details</legend>
+
+      <div>
+        <%= f.label :name %>:
+        <%= f.text_field :name, :size => 40 %>
+      </div>
+
+      <div>
+        <%= f.label :user_password, 'Password' %>:
+        <%= f.password_field :password, :size => 40 %>
+      </div>
+      <div>
+        <%= f.label :user_password_confirmation, 'Confirm' %>:
+        <%= f.password_field :password_confirmation, :size => 40 %>
+      </div>
+      <div>
+        <%= f.submit "Update" %>
+      </div>
+
+    </fieldset>
+  <% end %>
+
+  <%= link_to 'Show', @user %> |
+  <%= link_to 'Back', users_path %>
+</div>
diff --git a/app/views/users/index.html.erb b/app/views/users/index.html.erb
new file mode 100644 (file)
index 0000000..a2311a5
--- /dev/null
@@ -0,0 +1,20 @@
+<h1>Listing users</h1>
+
+<table>
+  <tr>
+    <th>Name</th>
+  </tr>
+
+<% for user in @users %>
+  <tr>
+    <td><%=h user.name %></td>
+    <td><%= link_to 'Show', user %></td>
+    <td><%= link_to 'Edit', edit_user_path(user) %></td>
+    <td><%= link_to 'Destroy', user, :confirm => 'Are you sure?', :method => :delete %></td>
+  </tr>
+<% end %>
+</table>
+
+<br />
+
+<%= link_to 'New user', new_user_path %>
diff --git a/app/views/users/new.html.erb b/app/views/users/new.html.erb
new file mode 100644 (file)
index 0000000..eaefc60
--- /dev/null
@@ -0,0 +1,30 @@
+<div class="depot-form">
+
+  <% form_for(@user) do |f| %>
+    <%= f.error_messages %>
+
+    <fieldset>
+      <legend>Enter user details</legend>
+
+      <div>
+        <%= f.label :name %>:
+        <%= f.text_field :name, :size => 40 %>
+      </div>
+
+      <div>
+        <%= f.label :user_password, 'Password' %>:
+        <%= f.password_field :password, :size => 40 %>
+      </div>
+      <div>
+        <%= f.label :user_password_confirmation, 'Confirm' %>:
+        <%= f.password_field :password_confirmation, :size => 40 %>
+      </div>
+      <div>
+        <%= f.submit "Create" %>
+      </div>
+
+    </fieldset>
+  <% end %>
+
+  <%= link_to 'Back', users_path %>
+</div>
diff --git a/app/views/users/show.html.erb b/app/views/users/show.html.erb
new file mode 100644 (file)
index 0000000..fc47069
--- /dev/null
@@ -0,0 +1,18 @@
+<p>
+  <b>Name:</b>
+  <%=h @user.name %>
+</p>
+
+<p>
+  <b>Hashed password:</b>
+  <%=h @user.hashed_password %>
+</p>
+
+<p>
+  <b>Salt:</b>
+  <%=h @user.salt %>
+</p>
+
+
+<%= link_to 'Edit', edit_user_path(@user) %> |
+<%= link_to 'Back', users_path %>
index 13bc70e430b6816f469f189ab053ef243a1d9fef..eef2afceaad134039d2323ee16f3d5c219feee68 100644 (file)
@@ -1,4 +1,10 @@
 ActionController::Routing::Routes.draw do |map|
+  map.resources :users
+
+  map.resources :line_items
+
+  map.resources :orders
+
   map.resources :products
 
   # The priority is based upon order of creation: first created -> highest priority.
diff --git a/db/migrate/20090311105014_create_users.rb b/db/migrate/20090311105014_create_users.rb
new file mode 100644 (file)
index 0000000..400d8da
--- /dev/null
@@ -0,0 +1,15 @@
+class CreateUsers < ActiveRecord::Migration
+  def self.up
+    create_table :users do |t|
+      t.string :name
+      t.string :hashed_password
+      t.string :salt
+
+      t.timestamps
+    end
+  end
+
+  def self.down
+    drop_table :users
+  end
+end
index d017a0990895df72b442cbbe26907ad3b8f3ef84..6b870e11873ebcdb925730695baa35b24b3c4719 100644 (file)
@@ -9,7 +9,7 @@
 #
 # It's strongly recommended to check this file into your version control system.
 
-ActiveRecord::Schema.define(:version => 20090304153613) do
+ActiveRecord::Schema.define(:version => 20090311105014) do
 
   create_table "line_items", :force => true do |t|
     t.integer  "product_id",                                :null => false
@@ -53,4 +53,12 @@ ActiveRecord::Schema.define(:version => 20090304153613) do
   add_index "sessions", ["session_id"], :name => "index_sessions_on_session_id"
   add_index "sessions", ["updated_at"], :name => "index_sessions_on_updated_at"
 
+  create_table "users", :force => true do |t|
+    t.string   "name"
+    t.string   "hashed_password"
+    t.string   "salt"
+    t.datetime "created_at"
+    t.datetime "updated_at"
+  end
+
 end
diff --git a/test/fixtures/users.yml b/test/fixtures/users.yml
new file mode 100644 (file)
index 0000000..ccf5bd1
--- /dev/null
@@ -0,0 +1,11 @@
+# Read about fixtures at http://ar.rubyonrails.org/classes/Fixtures.html
+
+one:
+  name: MyString
+  hashed_password: MyString
+  salt: MyString
+
+two:
+  name: MyString
+  hashed_password: MyString
+  salt: MyString
diff --git a/test/functional/admin_controller_test.rb b/test/functional/admin_controller_test.rb
new file mode 100644 (file)
index 0000000..9bbf29b
--- /dev/null
@@ -0,0 +1,8 @@
+require 'test_helper'
+
+class AdminControllerTest < ActionController::TestCase
+  # Replace this with your real tests.
+  test "the truth" do
+    assert true
+  end
+end
diff --git a/test/functional/users_controller_test.rb b/test/functional/users_controller_test.rb
new file mode 100644 (file)
index 0000000..c4a4804
--- /dev/null
@@ -0,0 +1,45 @@
+require 'test_helper'
+
+class UsersControllerTest < ActionController::TestCase
+  test "should get index" do
+    get :index
+    assert_response :success
+    assert_not_nil assigns(:users)
+  end
+
+  test "should get new" do
+    get :new
+    assert_response :success
+  end
+
+  test "should create user" do
+    assert_difference('User.count') do
+      post :create, :user => { }
+    end
+
+    assert_redirected_to user_path(assigns(:user))
+  end
+
+  test "should show user" do
+    get :show, :id => users(:one).id
+    assert_response :success
+  end
+
+  test "should get edit" do
+    get :edit, :id => users(:one).id
+    assert_response :success
+  end
+
+  test "should update user" do
+    put :update, :id => users(:one).id, :user => { }
+    assert_redirected_to user_path(assigns(:user))
+  end
+
+  test "should destroy user" do
+    assert_difference('User.count', -1) do
+      delete :destroy, :id => users(:one).id
+    end
+
+    assert_redirected_to users_path
+  end
+end
diff --git a/test/unit/user_test.rb b/test/unit/user_test.rb
new file mode 100644 (file)
index 0000000..a64d2d3
--- /dev/null
@@ -0,0 +1,8 @@
+require 'test_helper'
+
+class UserTest < ActiveSupport::TestCase
+  # Replace this with your real tests.
+  test "the truth" do
+    assert true
+  end
+end