Sunday, July 16, 2017

Chapter 10 In My Own Style

* First, I started the web server instance in myapp2
$ cd myapp2
$ rails s -b 0.0.0.0 -p 3000

* In Firefox, I typed: http://localhost:3000/

* I started Brackets and I changed the project folder to myapp2.

* I went to Michael Hartl's Ruby on Rails tutorial. https://www.railstutorial.org/book

* I went to Chapter 10 Updating, showing, and deleting users

* I created a branch called updating-users
$ git checkout -b updating-users

* I changed the heading of the <h2> tag to "Welcome to the myapp2".
myapp2/app/views/static_pages/home.html.erb
<h2 class="tm-gold-text tm-title">Welcome to the myapp2</h2>

* I changed the header of the website to myapp2.
myapp2/app/views/layouts/_header.html.erb
<%= link_to("myapp2", root_path, :class => "navbar-brand tm-site-name") %>

* I copied and pasted the codes in User Controllers and then I made some minor changes to split the name field into first_name and last_name.
myapp2/app/controllers/users_controller.rb 
    def user_params
      params.require(:user).permit(:first_name, :last_name, :email, :password,
                                   :password_confirmation)
    end

* I created an Edit view file for editing user profile. I copied and pasted the codes and split the name field into first_name and last_name.
myapp2/app/views/users/edit.html.erb
      <%= f.label :first_name %>
      <%= f.text_field :first_name, class: 'form-control' %>     

      <%= f.label :last_name %>
      <%= f.text_field :last_name, class: 'form-control' %>

* I added the edit_user_path() for the Settings menu in the header.
myapp2/app/views/layouts/_header.html.erb
<li><%= link_to "Settings", edit_user_path(current_user) %></li>

* I generated the Integration Test Suite to test User Edit function.
$ rails generate integration_test users_edit
Running via Spring preloader in process 5208
      invoke  test_unit
      create    test/integration/users_edit_test.rb

* I copied and pasted the codes in User Edit Integration Test Suite and I separated the name field into first_name and last_name.
test/integration/users_edit_test.rb
require 'test_helper'

class UsersEditTest < ActionDispatch::IntegrationTest

  def setup
    @user = users(:jimmy)
  end

  test "unsuccessful edit" do
    log_in_as(@user)
    get edit_user_path(@user)
    assert_template 'users/edit'
    patch user_path(@user), params: { user: { first_name:  "",
                                              last_name: "",
                                              email: "foo@invalid",
                                              password:              "foo",
                                              password_confirmation: "bar" } }

    assert_template 'users/edit'
  end   

  test "successful edit" do
    log_in_as(@user) 
    get edit_user_path(@user)
    assert_template 'users/edit'
    firstName = "Foo"
    lastName = "Bar"
    email = "foo@bar.com"
    patch user_path(@user), params: { user: { first_name:  firstName,
                                              last_name:  lastName,
                                              email: email,
                                              password:              "",
                                              password_confirmation: "" } }
    assert_not flash.empty?
    assert_redirected_to @user
    @user.reload
    assert_equal firtName, @user.first_name
    assert_equal lastName, @user.last_name
    assert_equal email, @user.email
  end   

  test "successful edit with friendly forwarding" do
    get edit_user_path(@user)
    log_in_as(@user)
    assert_redirected_to edit_user_url(@user)
    firstName = "Foo"
    lastName = "Bar"
    email = "foo@bar.com"
    patch user_path(@user), params: { user: { first_name:  firstName,
                                              last_name: lastName,
                                              email: email,
                                              password:              "",
                                              password_confirmation: "" } }
    assert_not flash.empty?
    assert_redirected_to @user
    @user.reload
    assert_equal firstName, @user.first_name
    assert_equal lastName, @user.last_name
    assert_equal email, @user.email
  end   
   
end

* I edited User Model to allow the allow_nil: true option to validates.
myapp2/app/models/user.rb
validates :password, presence: true, length: { minimum: 6 },  allow_nil: true

* I copied and pasted the codes in User Controller Test Suite and separated the name field into first_name and last_name.
myapp2/test/controllers/users_controller_test.rb
  test "should redirect update when not logged in" do
    patch user_path(@user), params: { user: { first_name: @user.first_name,
                                              last_name: @user.last_name,
                                              email: @user.email } }
    assert_not flash.empty?
    assert_redirected_to login_url
  end

* I copied and pasted the Test Fixture file and I separated the name field into first_name and last_name
myapp2/test/fixtures/users.yml
jimmy:
  first_name: Jimmy
  last_name: Chong
  email: jimmyc5@example.com
  password_digest: <%= User.digest('password') %>
 
michael:
  first_name: Michael
  last_name: Example
  email: michael@example.com
  password_digest: <%= User.digest('password') %>
  admin: true

archer:
  first_name: Sterling
  last_name: Archer
  email: duchess@example.com
  password_digest: <%= User.digest('password') %>

lana:
  first_name: Lana
  last_name: Kane
  email: hands@example.com
  password_digest: <%= User.digest('password') %>

malory:
  first_name: Malory
  last_name: Archer
  email: boss@example.com
  password_digest: <%= User.digest('password') %>

 <% 30.times do |n| %>
 user_<%= n %>:
  first_name:  <%= "User.first_name #{n}" %>
  last_name: <%= "User.last_name #{n}" %>
  email: <%= "user-#{n}@example.com" %>
  password_digest: <%= User.digest('password') %>
 <% end %>

* In the Session Helper class, I copied the def current_user?(user) and def redirect_back_or(default) actions.
myapp2/app/helpers/sessions_helper.rb
  # Returns true if the given user is the current user.
  def current_user?(user)
    user == current_user
  end

  # Redirects to stored location (or to the default).
  def redirect_back_or(default)
    redirect_to(session[:forwarding_url] || default)
    session.delete(:forwarding_url)
  end

* One more action called def store_location was copied to Session Helper
myapp2/app/helpers/sessions_helper.rb
  # Stores the URL trying to be accessed.
  def store_location
    session[:forwarding_url] = request.original_url if request.get?
  end

* In Session Controller, I updated the action to redirect_back_or user for friendly forwarding.
myapp2/app/controllers/sessions_controller.rb
redirect_back_or user

* I created and copied the codes in User Index file index.html.erb for listing out all users.
myapp2/app/views/users/index.html.erb
<% provide(:title, 'All users') %>
<h1>All users</h1>

<%= will_paginate %>

<ul class="users">
  <%= render @users %>
</ul>

<%= will_paginate %>

* I copied and pasted the codes in users_helper.rb for Gravatars.
app/helpers/users_helper.rb
module UsersHelper

  # Returns the Gravatar for the given user.
  def gravatar_for(user, options = { size: 80 })
    gravatar_id = Digest::MD5::hexdigest(user.email.downcase)
    size = options[:size]
    gravatar_url = "https://secure.gravatar.com/avatar/#{gravatar_id}?s=#{size}"
    image_tag(gravatar_url, alt: user.first_name, class: "gravatar")
  end   
   
end

* Also, I defined the formatting and style for the .user class when listing out all users.
myapp2/app/assets/stylesheets/templatemo-style.css
/* Users index */

.users {
  list-style: none;
  margin: 0;
  li {
    overflow: auto;
    padding: 10px 0;
    border-bottom: 1px solid $gray-lighter;
  }
}

* I assigned the user_path for the Users menu in the Header.
myapp2/app/views/layouts/_header.html.erb
<li class="nav-item"><%= link_to "Users", users_path, class: "nav-link" %></li>

* I added 'faker', 'will_paginate', and 'bootstrap-will_paginate' in the Gemfile.
myapp2/Gemfile
# faker for creating sample users for testing
gem 'faker',          '1.7.3'

# For pagination
gem 'will_paginate',           '3.1.5'
gem 'bootstrap-will_paginate', '1.0.0'

* Then, I ran bundle install.
$ bundle install

* I copied and pasted a program for seeding the database with sample users. I also separated and first_name and last_name field.
myapp2/db/seeds.rb
User.create!(first_name:  "Example",
             last_name: "User",
             email: "example@railstutorial.org",
             password:              "foobar",
             password_confirmation: "foobar")

99.times do |n|
  firstName = Faker::Name.first_name
  lastName = Faker::Name.last_name
  email = "example-#{n+1}@railstutorial.org"
  password = "password"
  User.create!(first_name:  firstName,
               last_name: lastName,
               email: email,
               password:              password,
               password_confirmation: password)
end

* After I had saved the seeds.rb file, I reset the database and then invoked the Rake task using db:seed
$ rails db:migrate:reset
$ rails db:seed

* I created a partial file to render a user.
myapp2/app/views/users/_user.html.erb
<li>
  <%= gravatar_for user, size: 50 %>
  <%= link_to user.name, user %>
</li>


* After I had run bundle install, I found Gem Faker was working, but Gem paginate wasn't. So, I re-started the Rails server.
Keyboard: Ctrl + C
$ rails s -b 0.0.0.0 -p 3000

* In the User View Partial file, I forgot to separate the first_name and last_name fields. I re-wrote it to match my data model.
myapp2/app/views/users/_user.html.erb
<li>
  <%= gravatar_for user, size: 50 %>
  <%= link_to user.first_name, user %>
</li>

* I generated an Integration Test for user_index to test users are listed out properly.
$ rails generate integration_test users_index
Running via Spring preloader in process 7001
      invoke  test_unit
      create    test/integration/users_index_test.rb

* I copied and pasted the code in User Index Integration Test Suite. As of the time being, I only verified the first_name field.
myapp2/test/integration/users_index_test.rb
require 'test_helper'

class UsersIndexTest < ActionDispatch::IntegrationTest

  def setup
    @admin     = users(:michael)
    @non_admin = users(:archer)     
  end

  test "index including pagination" do
    log_in_as(@non_admin)
    get users_path
    assert_template 'users/index'
    assert_select 'div.pagination', count: 2
    User.paginate(page: 1).each do |user|
      assert_select 'a[href=?]', user_path(user), text: user.first_name
    end
  end   

  test "index as admin including pagination and delete links" do
    log_in_as(@admin)
    get users_path
    assert_template 'users/index'
    assert_select 'div.pagination'
    first_page_of_users = User.paginate(page: 1)
    first_page_of_users.each do |user|
      assert_select 'a[href=?]', user_path(user), text: user.first_name
      unless user == @admin
        assert_select 'a[href=?]', user_path(user), text: 'delete'
      end
    end
    assert_difference 'User.count', -1 do
      delete user_path(@non_admin)
    end
  end

  test "index as non-admin" do
    log_in_as(@non_admin)
    get users_path
    assert_select 'a', text: 'delete', count: 0
  end   
   
end

* I added the admin column for the User model to specify a user with admin privilege.
$ rails generate migration add_admin_to_users admin:boolean
Running via Spring preloader in process 7098
      invoke  active_record
      create    db/migrate/20170717004219_add_admin_to_users.rb

* I added default: false for the admin column.
myapp2/db/migrate
class AddAdminToUsers < ActiveRecord::Migration[5.0]
  def change
    add_column :users, :admin, :boolean, default: false
  end
end

* Then, I ran DB migrate.
$ rails db:migrate
== 20170717004219 AddAdminToUsers: migrating ==================================
-- add_column(:users, :admin, :boolean, {:default=>false})
   -> 0.0056s
== 20170717004219 AddAdminToUsers: migrated (0.0064s) =========================

* I added admin: true for the Example User.
myapp2/db/seeds.rb
User.create!(first_name:  "Example",
             last_name: "User",
             email: "example@railstutorial.org",
             password:              "foobar",
             password_confirmation: "foobar",
             admin: true)

* Then, I reset the database and re-did the database seeding.
$ rails db:migrate:reset
$ rails db:seed

* Then, I added the delete links in the user views that is only accessible by admin.
myapp2/app/views/users/_user.html.erb
<li>
  <%= gravatar_for user, size: 50 %>
  <%= link_to user.first_name, user %>
  <% if current_user.admin? && !current_user?(user) %>
    | <%= link_to "delete", user, method: :delete,
                                  data: { confirm: "You sure?" } %>
  <% end %>   
</li>

* When I ran a test, there were error of
Error:
UsersIndexTest#test_index_including_pagination:
ActionView::Template::Error: Undefined variable: "$gray-lighter".
    app/views/layouts/application.html.erb:6:in `_app_views_layouts_application_html_erb__254424222595337930_47226138909680'
    test/integration/users_index_test.rb:12:in `block in <class:UsersIndexTest>'

* Later, I found it Undefined variable: "$gray-lighter" is in the CSS style.
app/assets/stytlesheets/templatemo-style.css
.users {
  list-style: none;
  margin: 0;
  li {
    overflow: auto;
    padding: 10px 0;
/*    border-bottom: 1px solid $gray-lighter; */
      border-bottom: 1px solid;
  }

* There were 2 failures that 'div.pagination' could not be found.
$ rails test
Running via Spring preloader in process 7821
Run options: --seed 37045

# Running:

........F

Failure:
UsersIndexTest#test_index_as_admin_including_pagination_and_delete_links [/home/jimmyc/myapp2/test/integration/users_index_test.rb:24]:
Expected at least 1 element matching "div.pagination", found 0..
Expected 0 to be >= 1.


bin/rails test test/integration/users_index_test.rb:20

F

Failure:
UsersIndexTest#test_index_including_pagination [/home/jimmyc/myapp2/test/integration/users_index_test.rb:14]:
Expected exactly 2 elements matching "div.pagination", found 0..
Expected: 2
  Actual: 0


bin/rails test test/integration/users_index_test.rb:10

..................................

Finished in 2.308248s, 19.0621 runs/s, 45.9223 assertions/s.

44 runs, 106 assertions, 2 failures, 0 errors, 0 skips

* As I check the HTML file (in Firefox -> Ctrl + U), I saw that there were some odd with the pagination. One is
<div class="pagination"><ul class="pagination">

* Then, I started the server instance of Michael Hartl's original sample_app. I found that the patterns were just the same...
<div class="pagination"><ul class="pagination">

* I made some editing and this was the final version of the fixture file for Users.yml
myapp2/test/fixtures/users.yml
malory:
  first_name: Malory
  last_name: Archer
  email: boss@example.com
  password_digest: <%= User.digest('password') %>

<% 30.times do |n| %>
user_<%= n %>:
  first_name:  <%= "User #{n}" %>
  last_name:  <%= "User #{n}" %> 
  email: <%= "user-#{n}@example.com" %>
  password_digest: <%= User.digest('password') %>
<% end %>

* In _user.html.erb partial file, I concatenated the first_name and last_name of the user.
myapp2/app/views/users/_user.html.erb
<%= link_to user.first_name + " " + user.last_name, user %>

* In User Index Integration Test Suite, I changed the test case to verify a full name that includes first_name and last_name.
myapp2/test/integration/users_index_test.rb
assert_select 'a[href=?]', user_path(user), text: user.first_name + " " + user.last_name


* After all, I ran the test to make sure no failures.
$ rails test
Running via Spring preloader in process 9902
Run options: --seed 41776

# Running:

.................................../home/jimmyc/.rbenv/versions/2.4.0/lib/ruby/gems/2.4.0/gems/will_paginate-3.1.5/lib/will_paginate/view_helpers/link_renderer.rb:27: warning: constant ::Fixnum is deprecated
/home/jimmyc/.rbenv/versions/2.4.0/lib/ruby/gems/2.4.0/gems/will_paginate-3.1.5/lib/will_paginate/view_helpers/link_renderer.rb:91: warning: constant ::Fixnum is deprecated
.........

Finished in 2.264808s, 19.4277 runs/s, 85.6585 assertions/s.

44 runs, 194 assertions, 0 failures, 0 errors, 0 skips

* I added all untracked files.
$ git add -A

* I committed the changes and marked it an bootstrap had not been installed.
$ git commit -m "No Boostrap Finish user edit, update, index, and destroy actions"

* I merged it back into Master branch.
$ git checkout master
$ git merge updating-users

* Lastly, I pushed it onto Github (https://github.com/jimmy2046/myapp2).
$ git push 

1 comment:

  1. it is very excellent blog and useful article thank you for sharing with us , keep posting Ruby on Rails Online Training India

    ReplyDelete

How to kill an abandoned process in Linux/Unix

I remembered it, then I forgot, then I remembered it, and then I forgot again. In case of a Linux/Unit process hang, I have to figure out ...