From d15c05ffa34ab7c733143e7e07c404a1bcaf111f Mon Sep 17 00:00:00 2001 From: kaku87 Date: Wed, 14 Aug 2013 00:16:18 +0900 Subject: [PATCH 1/7] add chapter 8 --- .../app/assets/javascripts/sessions.js.coffee | 3 ++ .../app/assets/stylesheets/custom.css.scss | 15 +++++++- .../app/assets/stylesheets/sessions.css.scss | 3 ++ .../app/controllers/application_controller.rb | 1 + .../app/controllers/sessions_controller.rb | 19 ++++++++++ .../app/controllers/users_controller.rb | 12 ++++--- .../sample_app/app/helpers/sessions_helper.rb | 7 ++++ kaku/sample_app/app/models/user.rb | 16 +++++++++ .../app/views/layouts/application.html.erb | 8 +++-- .../app/views/sessions/new.html.erb | 19 ++++++++++ .../app/views/shared/_error_messages.html.erb | 12 +++++++ .../config/environments/production.rb | 2 +- kaku/sample_app/config/routes.rb | 6 ++-- ...30813135754_add_remember_token_to_users.rb | 4 +++ kaku/sample_app/db/schema.rb | 2 +- kaku/sample_app/spec/models/user_spec.rb | 5 +++ .../requests/authentication_pages_spec.rb | 35 +++++++++++++++++++ 17 files changed, 157 insertions(+), 12 deletions(-) create mode 100644 kaku/sample_app/app/assets/javascripts/sessions.js.coffee create mode 100644 kaku/sample_app/app/assets/stylesheets/sessions.css.scss create mode 100644 kaku/sample_app/app/controllers/sessions_controller.rb create mode 100644 kaku/sample_app/app/helpers/sessions_helper.rb create mode 100644 kaku/sample_app/app/views/sessions/new.html.erb create mode 100644 kaku/sample_app/app/views/shared/_error_messages.html.erb create mode 100644 kaku/sample_app/db/migrate/20130813135754_add_remember_token_to_users.rb create mode 100644 kaku/sample_app/spec/requests/authentication_pages_spec.rb diff --git a/kaku/sample_app/app/assets/javascripts/sessions.js.coffee b/kaku/sample_app/app/assets/javascripts/sessions.js.coffee new file mode 100644 index 0000000..24f83d1 --- /dev/null +++ b/kaku/sample_app/app/assets/javascripts/sessions.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://coffeescript.org/ diff --git a/kaku/sample_app/app/assets/stylesheets/custom.css.scss b/kaku/sample_app/app/assets/stylesheets/custom.css.scss index a65aba3..e24f2f5 100644 --- a/kaku/sample_app/app/assets/stylesheets/custom.css.scss +++ b/kaku/sample_app/app/assets/stylesheets/custom.css.scss @@ -134,4 +134,17 @@ input, textarea, select, .uneditable-input { input { height: auto !important; -} \ No newline at end of file +} + +#error_explanation { + color: #f00; + ul { + list-style: none; + margin: 0 0 18px 0; + } +} + +.field_with_errors { + @extend .control-group; + @extend .error; + } \ No newline at end of file diff --git a/kaku/sample_app/app/assets/stylesheets/sessions.css.scss b/kaku/sample_app/app/assets/stylesheets/sessions.css.scss new file mode 100644 index 0000000..ccb1ed2 --- /dev/null +++ b/kaku/sample_app/app/assets/stylesheets/sessions.css.scss @@ -0,0 +1,3 @@ +// Place all the styles related to the Sessions controller here. +// They will automatically be included in application.css. +// You can use Sass (SCSS) here: http://sass-lang.com/ diff --git a/kaku/sample_app/app/controllers/application_controller.rb b/kaku/sample_app/app/controllers/application_controller.rb index d83690e..d8b3d09 100644 --- a/kaku/sample_app/app/controllers/application_controller.rb +++ b/kaku/sample_app/app/controllers/application_controller.rb @@ -2,4 +2,5 @@ class ApplicationController < ActionController::Base # Prevent CSRF attacks by raising an exception. # For APIs, you may want to use :null_session instead. protect_from_forgery with: :exception + include SessionsHelper end diff --git a/kaku/sample_app/app/controllers/sessions_controller.rb b/kaku/sample_app/app/controllers/sessions_controller.rb new file mode 100644 index 0000000..200bb2e --- /dev/null +++ b/kaku/sample_app/app/controllers/sessions_controller.rb @@ -0,0 +1,19 @@ +class SessionsController < ApplicationController + def new + end + + def create + user = User.find_by(email: params[:session][:email].downcase) + if user && user.authenticate(params[:session][:password]) + # Sign the user in and redirect to the user's show page. + sign_in user + redirect_to user + else + flash.now[:error] = 'Invalid email/password combination' + render 'new' + end + end + + def destroy + end +end diff --git a/kaku/sample_app/app/controllers/users_controller.rb b/kaku/sample_app/app/controllers/users_controller.rb index 9e15c6b..887375a 100644 --- a/kaku/sample_app/app/controllers/users_controller.rb +++ b/kaku/sample_app/app/controllers/users_controller.rb @@ -8,16 +8,18 @@ def show end def create - @user = User.new(params[:user]) # Not the final implementation! + @user = User.new(user_params) # Not the final implementation! if @user.save # Handle a successful save. + flash[:success] = "Welcome to the Sample App!" + redirect_to @user else render 'new' end end - + private - def user_params - params.require(:user).permit(:name, :email, :password, :password_confirmation) - end + def user_params + params.require(:user).permit(:name, :email, :password, :password_confirmation) + end end diff --git a/kaku/sample_app/app/helpers/sessions_helper.rb b/kaku/sample_app/app/helpers/sessions_helper.rb new file mode 100644 index 0000000..8ca35ed --- /dev/null +++ b/kaku/sample_app/app/helpers/sessions_helper.rb @@ -0,0 +1,7 @@ +module SessionsHelper + def sign_in(user) + remember_token = User.new_remember_token + cookies.permanent[:remember_token] = remember_token + user.update_attribute(:remember_token, User.encrypt(remember_token)) + self.current_user = user + end diff --git a/kaku/sample_app/app/models/user.rb b/kaku/sample_app/app/models/user.rb index f30c482..1c544eb 100644 --- a/kaku/sample_app/app/models/user.rb +++ b/kaku/sample_app/app/models/user.rb @@ -1,8 +1,24 @@ class User < ActiveRecord::Base before_save { self.email = email.downcase } + before_create :create_remember_token + validates :name, presence: true, length: { maximum: 50 } VALID_EMAIL_REGEX = /\A[\w+\-.]+@[a-z\d\-.]+\.[a-z]+\z/i validates :email, presence: true, format:{with: VALID_EMAIL_REGEX}, uniqueness: { case_sensitive: false} validates :password, length: { minimum: 6 } has_secure_password + + def User.new_remember_token + SecureRandom.urlsafe_base64 + end + + def User.encrypt(token) + Digest::SHA1.hexdigest(token.to_s) + end + + private + + def create_remember_token + self.remember_token = User.encrypt(User.new_remember_token) + end end diff --git a/kaku/sample_app/app/views/layouts/application.html.erb b/kaku/sample_app/app/views/layouts/application.html.erb index 7f6ed7b..1b9ee35 100644 --- a/kaku/sample_app/app/views/layouts/application.html.erb +++ b/kaku/sample_app/app/views/layouts/application.html.erb @@ -11,8 +11,12 @@ <%= render 'layouts/header' %>
- <%= yield %> - <%= debug(params) if Rails.env.development? %> + <% flash.each do |key, value| %> +
<%= value %>
+ <% end %> + <%= yield %> + <%= render 'layouts/footer' %> + <%= debug(params) if Rails.env.development? %>
\ No newline at end of file diff --git a/kaku/sample_app/app/views/sessions/new.html.erb b/kaku/sample_app/app/views/sessions/new.html.erb new file mode 100644 index 0000000..861efb8 --- /dev/null +++ b/kaku/sample_app/app/views/sessions/new.html.erb @@ -0,0 +1,19 @@ +<% provide(:title, "Sign in") %> +

Sign in

+ +
+
+ <%= form_for(:session, url: sessions_path) do |f| %> + + <%= f.label :email %> + <%= f.text_field :email %> + + <%= f.label :password %> + <%= f.password_field :password %> + + <%= f.submit "Sign in", class: "btn btn-large btn-primary" %> + <% end %> + +

New user? <%= link_to "Sign up now!", signup_path %>

+
+
\ No newline at end of file diff --git a/kaku/sample_app/app/views/shared/_error_messages.html.erb b/kaku/sample_app/app/views/shared/_error_messages.html.erb new file mode 100644 index 0000000..c9c0ff4 --- /dev/null +++ b/kaku/sample_app/app/views/shared/_error_messages.html.erb @@ -0,0 +1,12 @@ +<% if @user.errors.any? %> +
+
+ The form contains <%= pluralize(@user.errors.count, "error") %>. +
+ +
+<% end %> \ No newline at end of file diff --git a/kaku/sample_app/config/environments/production.rb b/kaku/sample_app/config/environments/production.rb index 5d0ee57..f112db5 100644 --- a/kaku/sample_app/config/environments/production.rb +++ b/kaku/sample_app/config/environments/production.rb @@ -40,7 +40,7 @@ # config.action_dispatch.x_sendfile_header = 'X-Accel-Redirect' # for nginx # Force all access to the app over SSL, use Strict-Transport-Security, and use secure cookies. - # config.force_ssl = true + config.force_ssl = true # Set to :debug to see everything in the log. config.log_level = :info diff --git a/kaku/sample_app/config/routes.rb b/kaku/sample_app/config/routes.rb index a87161d..1721395 100644 --- a/kaku/sample_app/config/routes.rb +++ b/kaku/sample_app/config/routes.rb @@ -1,12 +1,14 @@ SampleApp::Application.routes.draw do resources :users root to: 'static_pages#home' + resources :sessions, only: [:new, :create, :destroy] match '/signup', to: 'users#new', via: 'get' + match '/signin', to: 'sessions#new', via: 'get' + match '/signout', to: 'sessions#destroy', via: 'delete' match '/help', to: 'static_pages#help', via: 'get' match '/about', to: 'static_pages#about', via: 'get' match '/contact', to: 'static_pages#contact', via: 'get' - - # The priority is based upon order of creation: first created -> highest priority. + # The priority is based upon order of creation: first created -> highest priority. # # # See how all your routes lay out with "rake routes". diff --git a/kaku/sample_app/db/migrate/20130813135754_add_remember_token_to_users.rb b/kaku/sample_app/db/migrate/20130813135754_add_remember_token_to_users.rb new file mode 100644 index 0000000..74c254f --- /dev/null +++ b/kaku/sample_app/db/migrate/20130813135754_add_remember_token_to_users.rb @@ -0,0 +1,4 @@ +class AddRememberTokenToUsers < ActiveRecord::Migration + def change + end +end diff --git a/kaku/sample_app/db/schema.rb b/kaku/sample_app/db/schema.rb index 1644516..3228446 100644 --- a/kaku/sample_app/db/schema.rb +++ b/kaku/sample_app/db/schema.rb @@ -11,7 +11,7 @@ # # It's strongly recommended that you check this file into your version control system. -ActiveRecord::Schema.define(version: 20130730151702) do +ActiveRecord::Schema.define(version: 20130813135754) do create_table "users", force: true do |t| t.string "name" diff --git a/kaku/sample_app/spec/models/user_spec.rb b/kaku/sample_app/spec/models/user_spec.rb index c764283..b90d84e 100644 --- a/kaku/sample_app/spec/models/user_spec.rb +++ b/kaku/sample_app/spec/models/user_spec.rb @@ -14,6 +14,7 @@ it { should respond_to(:password_digest) } it { should respond_to(:password) } it { should respond_to(:password_confirmation) } + it { should respond_to(:remember_token) } it { should respond_to(:authenticate) } describe "when name is not presence" do @@ -82,5 +83,9 @@ end end + describe "remember token" do + before { @user.save } + its(:remember_token) { should_not be_blank } + end end diff --git a/kaku/sample_app/spec/requests/authentication_pages_spec.rb b/kaku/sample_app/spec/requests/authentication_pages_spec.rb new file mode 100644 index 0000000..c446650 --- /dev/null +++ b/kaku/sample_app/spec/requests/authentication_pages_spec.rb @@ -0,0 +1,35 @@ +require 'spec_helper' + +describe "Authentication" do + + subject { page } + + describe "signin page" do + before { visit signin_path } + + it { should have_content('Sign in') } + it { should have_title('Sign in') } + end +end + +describe "signin" do + before { visit signin_path } + + describe "with invalid information" do + before { click_button "Sign in" } + + it { should have_title('Sign in') } + it { should have_selector('div.alert.alert-error', text: 'Invalid') } + + describe "after visiting another page" do + before { click_link "Home" } + it { should_not have_selector('div.alert.alert-error') } + end + end + + it { should have_title(user.name) } + it { should have_link('Profile', href: user_path(user)) } + it { should have_link('Sign out', href: signout_path) } + it { should_not have_link('Sign in', href: signin_path) } + end +end \ No newline at end of file From ec16d26000ff1a57972e5dea293cb960719104e6 Mon Sep 17 00:00:00 2001 From: kaku87 Date: Fri, 16 Aug 2013 00:18:26 +0900 Subject: [PATCH 2/7] add chapter 8 --- kaku/sample_app/Gemfile | 1 + kaku/sample_app/Gemfile.lock | 5 +++ .../app/assets/javascripts/application.js | 1 + .../app/controllers/sessions_controller.rb | 2 + .../app/controllers/users_controller.rb | 12 +++++- .../sample_app/app/helpers/sessions_helper.rb | 19 +++++++++ .../app/views/layouts/_header.html.erb | 41 +++++++++++++------ ...30815143550_add_remember_token_to_users.rb | 6 +++ kaku/sample_app/db/schema.rb | 4 +- .../requests/authentication_pages_spec.rb | 36 +++++++--------- .../spec/requests/user_pages_spec.rb | 9 ++++ 11 files changed, 101 insertions(+), 35 deletions(-) create mode 100644 kaku/sample_app/db/migrate/20130815143550_add_remember_token_to_users.rb diff --git a/kaku/sample_app/Gemfile b/kaku/sample_app/Gemfile index 81dd1a6..2c779e7 100644 --- a/kaku/sample_app/Gemfile +++ b/kaku/sample_app/Gemfile @@ -13,6 +13,7 @@ group :development, :test do end group :test do + gem 'webrat' gem 'selenium-webdriver', '2.0.0' gem 'capybara', '2.1.0' gem 'guard-rspec','2.5.0' diff --git a/kaku/sample_app/Gemfile.lock b/kaku/sample_app/Gemfile.lock index 51d2f54..13e4096 100644 --- a/kaku/sample_app/Gemfile.lock +++ b/kaku/sample_app/Gemfile.lock @@ -186,6 +186,10 @@ GEM uglifier (2.1.1) execjs (>= 0.3.0) multi_json (~> 1.0, >= 1.0.2) + webrat (0.7.3) + nokogiri (>= 1.2.0) + rack (>= 1.0) + rack-test (>= 0.5.3) xpath (2.0.0) nokogiri (~> 1.3) @@ -213,3 +217,4 @@ DEPENDENCIES sqlite3 (= 1.3.7) turbolinks (= 1.1.1) uglifier (= 2.1.1) + webrat diff --git a/kaku/sample_app/app/assets/javascripts/application.js b/kaku/sample_app/app/assets/javascripts/application.js index d6925fa..e3a9f2d 100644 --- a/kaku/sample_app/app/assets/javascripts/application.js +++ b/kaku/sample_app/app/assets/javascripts/application.js @@ -12,5 +12,6 @@ // //= require jquery //= require jquery_ujs +//= require bootstrap //= require turbolinks //= require_tree . diff --git a/kaku/sample_app/app/controllers/sessions_controller.rb b/kaku/sample_app/app/controllers/sessions_controller.rb index 200bb2e..a6ad58d 100644 --- a/kaku/sample_app/app/controllers/sessions_controller.rb +++ b/kaku/sample_app/app/controllers/sessions_controller.rb @@ -15,5 +15,7 @@ def create end def destroy + sign_out + redirect_to root_path end end diff --git a/kaku/sample_app/app/controllers/users_controller.rb b/kaku/sample_app/app/controllers/users_controller.rb index 887375a..717bbb2 100644 --- a/kaku/sample_app/app/controllers/users_controller.rb +++ b/kaku/sample_app/app/controllers/users_controller.rb @@ -10,7 +10,7 @@ def show def create @user = User.new(user_params) # Not the final implementation! if @user.save - # Handle a successful save. + sign_in @user flash[:success] = "Welcome to the Sample App!" redirect_to @user else @@ -18,6 +18,16 @@ def create end end + def destroy + sign_out + redirect_to root_path + end + + def sign_out + self.current_user = nil + cookies.delete(:remember_token) + end + private def user_params params.require(:user).permit(:name, :email, :password, :password_confirmation) diff --git a/kaku/sample_app/app/helpers/sessions_helper.rb b/kaku/sample_app/app/helpers/sessions_helper.rb index 8ca35ed..84350de 100644 --- a/kaku/sample_app/app/helpers/sessions_helper.rb +++ b/kaku/sample_app/app/helpers/sessions_helper.rb @@ -5,3 +5,22 @@ def sign_in(user) user.update_attribute(:remember_token, User.encrypt(remember_token)) self.current_user = user end + + def signed_in? + !current_user.nil? + end + + def current_user=(user) + @current_user = user + end + + def current_user + remember_token = User.encrypt(cookies[:remember_token]) + @current_user ||= User.find_by(remember_token: remember_token) + end + + def sign_out + self.current_user = nil + cookies.delete(:remember_token) + end +end \ No newline at end of file diff --git a/kaku/sample_app/app/views/layouts/_header.html.erb b/kaku/sample_app/app/views/layouts/_header.html.erb index 2a56299..a7a734f 100644 --- a/kaku/sample_app/app/views/layouts/_header.html.erb +++ b/kaku/sample_app/app/views/layouts/_header.html.erb @@ -1,14 +1,31 @@ + \ No newline at end of file diff --git a/kaku/sample_app/db/migrate/20130815143550_add_remember_token_to_users.rb b/kaku/sample_app/db/migrate/20130815143550_add_remember_token_to_users.rb new file mode 100644 index 0000000..8a62dcd --- /dev/null +++ b/kaku/sample_app/db/migrate/20130815143550_add_remember_token_to_users.rb @@ -0,0 +1,6 @@ +class AddRememberTokenToUsers < ActiveRecord::Migration + def change + add_column :users, :remember_token, :string + add_index :users, :remember_token + end +end diff --git a/kaku/sample_app/db/schema.rb b/kaku/sample_app/db/schema.rb index 3228446..4076ffe 100644 --- a/kaku/sample_app/db/schema.rb +++ b/kaku/sample_app/db/schema.rb @@ -11,7 +11,7 @@ # # It's strongly recommended that you check this file into your version control system. -ActiveRecord::Schema.define(version: 20130813135754) do +ActiveRecord::Schema.define(version: 20130815143550) do create_table "users", force: true do |t| t.string "name" @@ -19,8 +19,10 @@ t.datetime "created_at" t.datetime "updated_at" t.string "password_digest" + t.string "remember_token" end add_index "users", ["email"], name: "index_users_on_email", unique: true + add_index "users", ["remember_token"], name: "index_users_on_remember_token" end diff --git a/kaku/sample_app/spec/requests/authentication_pages_spec.rb b/kaku/sample_app/spec/requests/authentication_pages_spec.rb index c446650..b175fe9 100644 --- a/kaku/sample_app/spec/requests/authentication_pages_spec.rb +++ b/kaku/sample_app/spec/requests/authentication_pages_spec.rb @@ -1,35 +1,29 @@ -require 'spec_helper' - describe "Authentication" do subject { page } - describe "signin page" do + describe "signin" do before { visit signin_path } - it { should have_content('Sign in') } - it { should have_title('Sign in') } - end -end + describe "with invalid information" do + before { click_button "Sign in" } -describe "signin" do - before { visit signin_path } + it { should have_title('Sign in') } + it { should have_selector('div.alert.alert-error', text: 'Invalid email/password combination') } + end - describe "with invalid information" do - before { click_button "Sign in" } + describe "with valid information" do + let(:user) { FactoryGirl.create(:user) } + before do + fill_in "Email", with: user.email.upcase + fill_in "Password", with: user.password + click_button "Sign in" + end - it { should have_title('Sign in') } - it { should have_selector('div.alert.alert-error', text: 'Invalid') } - - describe "after visiting another page" do - before { click_link "Home" } - it { should_not have_selector('div.alert.alert-error') } - end - end - - it { should have_title(user.name) } + it { should have_title(user.name) } it { should have_link('Profile', href: user_path(user)) } it { should have_link('Sign out', href: signout_path) } it { should_not have_link('Sign in', href: signin_path) } end + end end \ No newline at end of file diff --git a/kaku/sample_app/spec/requests/user_pages_spec.rb b/kaku/sample_app/spec/requests/user_pages_spec.rb index df9c173..5f39cf5 100644 --- a/kaku/sample_app/spec/requests/user_pages_spec.rb +++ b/kaku/sample_app/spec/requests/user_pages_spec.rb @@ -42,6 +42,15 @@ it "should create a user" do expect { click_button submit }.to change(User, :count).by(1) end + + describe "after saving the user" do + before { click_button submit } + let(:user) { User.find_by(email: 'user@example.com') } + + it { should have_link('Sign out') } + it { should have_title(user.name) } + it { should have_selector('div.alert.alert-success', text: 'Welcome') } + end end end From cbaa9f127789714078dee2e8b26528e7186e8b34 Mon Sep 17 00:00:00 2001 From: kaku87 Date: Fri, 23 Aug 2013 04:30:35 +0900 Subject: [PATCH 3/7] add chapter 9 --- .../app/controllers/users_controller.rb | 32 ++++-- .../app/views/layouts/_header.html.erb | 4 +- kaku/sample_app/app/views/users/edit.html.erb | 28 +++++ .../requests/authentication_pages_spec.rb | 23 +++- .../spec/requests/static_pages_spec.rb | 2 +- .../spec/requests/user_pages_spec.rb | 101 +++++++++++------- kaku/sample_app/spec/support/utilities.rb | 14 +++ 7 files changed, 156 insertions(+), 48 deletions(-) create mode 100644 kaku/sample_app/app/views/users/edit.html.erb diff --git a/kaku/sample_app/app/controllers/users_controller.rb b/kaku/sample_app/app/controllers/users_controller.rb index 717bbb2..d3175ea 100644 --- a/kaku/sample_app/app/controllers/users_controller.rb +++ b/kaku/sample_app/app/controllers/users_controller.rb @@ -2,11 +2,11 @@ class UsersController < ApplicationController def new @user = User.new end - + def show - @user = User.find(params[:id]) + @user = User.find(params[:id]) end - + def create @user = User.new(user_params) # Not the final implementation! if @user.save @@ -22,14 +22,30 @@ def destroy sign_out redirect_to root_path end - + def sign_out self.current_user = nil cookies.delete(:remember_token) end - + + def edit + @user = User.find(params[:id]) + end + + def update + @user = User.find(params[:id]) + if @user.update_attributes(user_params) + flash[:success] = "Profile updated" + sign_in @user + redirect_to @user + else + render 'edit' + + end + end + private - def user_params - params.require(:user).permit(:name, :email, :password, :password_confirmation) - end + def user_params + params.require(:user).permit(:name, :email, :password, :password_confirmation) + end end diff --git a/kaku/sample_app/app/views/layouts/_header.html.erb b/kaku/sample_app/app/views/layouts/_header.html.erb index a7a734f..4520677 100644 --- a/kaku/sample_app/app/views/layouts/_header.html.erb +++ b/kaku/sample_app/app/views/layouts/_header.html.erb @@ -14,7 +14,7 @@