Introduction
ในส่วนแรกของ tutorial เราเรียนรู้การติดตั้ง Devise และ setup ใน Rails application ในส่วนนี้ เราจะดูวิธี integrate DeviseInvitable
DeviseInvitable เป็น extension ที่ทำงานกับ Devise ด้วย DeviseInvitable ในแอป users สามารถเชิญเพื่อนผ่าน emails ได้ ฟีเจอร์นี้เก๋บมากสำหรับ collaboration app
Setting Up DeviseInvitable
เปิด Gemfile เพิ่ม gem:
#Gemfile ... gem 'devise_invitable'
รัน bundle install
รัน generator command เพิ่ม DeviseInvitable config options ไปใน Devise config file
rails generate devise_invitable:install
ดูการเปลี่ยนแปลงใน config/initializers/devise.rb
ต่อไป เพิ่ม DeviseInvitable ให้ User model
rails generate devise_invitable User
จะเพิ่ม flag :invitable ให้ model User model จะเป็น:
#app/models/user.rb class User < ActiveRecord::Base # Include default devise modules. Others available are: # :confirmable, :lockable, :timeoutable and :omniauthable devise :invitable, :database_authenticatable, :registerable, :recoverable, :rememberable, :trackable, :validatable end
Command ด้านบนยัง generate migration file แบบนี้:
class DeviseInvitableAddToUsers < ActiveRecord::Migration def up change_table :users do |t| t.string :invitation_token t.datetime :invitation_created_at t.datetime :invitation_sent_at t.datetime :invitation_accepted_at t.integer :invitation_limit t.references :invited_by, polymorphic: true t.integer :invitations_count, default: 0 t.index :invitations_count t.index :invitation_token, unique: true # for invitable t.index :invited_by_id end end def down change_table :users do |t| t.remove_references :invited_by, polymorphic: true t.remove :invitations_count, :invitation_limit, :invitation_sent_at, :invitation_accepted_at, :invitation_token, :invitation_created_at end end end
Migrate database ด้วย rake db:migrate
Configuring the Controller for DeviseInvitable
DeviseInvitable ต้องการ parameters ตอนส่ง invite ต้อง whitelist parameters ที่จำเป็น แก้ app/controllers/application_controller.rb ให้เป็น:
#app/controllers/application_controller.rb class ApplicationController < ActionController::Base protect_from_forgery with: :exception before_action :configure_permitted_parameters, if: :devise_controller? protected def configure_permitted_parameters added_attrs = [:username, :email, :password, :password_confirmation, :remember_me] devise_parameter_sanitizer.permit :sign_up, keys: added_attrs devise_parameter_sanitizer.permit :account_update, keys: added_attrs devise_parameter_sanitizer.permit :accept_invitation, keys: [:email] end end
จากด้านบน เห็นว่า :email whitelisted สำหรับ DeviseInvitable
ลองใน console รัน rails console ใส่:
[1] pry(main)> User.invite!(:email => "johndoe@example.com")
ควรได้ output แบบนี้ (อาจต่างกันนิดหน่อย):
[2] pry(main)> User Load (78.3ms) SELECT "users".* FROM "users" WHERE "users"."email" = ? ORDER BY "users"."id" ASC LIMIT 1 [["email", "johndoe@example.com"]] User Load (0.2ms) SELECT "users".* FROM "users" WHERE "users"."invitation_token" = ? ORDER BY "users"."id" ASC LIMIT 1 [["invitation_token", "658da470d5fcbb2275f30bc1fb66f5771b889cec2f1e56f536319d2fd1ef4a92"]] (0.1ms) begin transaction SQL (67.8ms) INSERT INTO "users" ("email", "encrypted_password", "invitation_token", "invitation_created_at", "invitation_sent_at", "created_at", "updated_at") VALUES (?, ?, ?, ?, ?, ?, ?) [["email", "johndoe@example.com"], ["encrypted_password", "$2a$11$0sLfqvfFDsebcmcQTUXzlOuqNIooL5z8niXeza8OUwNK3gZY/iRum"], ["invitation_token", "658da470d5fcbb2275f30bc1fb66f5771b889cec2f1e56f536319d2fd1ef4a92"], ["invitation_created_at", "2016-10-07 07:41:51.254047"], ["invitation_sent_at", "2016-10-07 07:41:51.254047"], ["created_at", "2016-10-07 07:41:51.255700"], ["updated_at", "2016-10-07 07:41:51.255700"]] (220.5ms) commit transaction Rendered ... invitation_instructions.html.erb ... Devise::Mailer#invitation_instructions: processed outbound mail in 247.1ms Sent mail to johndoe@example.com (74.3ms) Date: Fri, 07 Oct 2016 08:41:51 +0100 From: please-change-me-at-config-initializers-devise@example.com ... Subject: Invitation instructions ... https://localhost:3000/users/invitation/accept?invitation_token=xmW9uRfyafptmeFMmFBy ... => #<User:0x00558d875fa798 id: 4, email: "johndoe@example.com", ... invitation_token: "658da470d5fcbb2275f30bc1fb66f5771b889cec2f1e56f536319d2fd1ef4a92", ...> [3] pry(main)> ทำงานตามแผน
ไม่ไหวให้ users ส่ง invitations ผ่าน command line ต้อง setup DeviseInvitable frontend รัน generator สร้าง views:
rails generate devise_invitable:views users
เพิ่ม link ไปหน้าส่ง invites (app/views/users/invitations/new.html.erb) ใน navigation file:
#app/views/shared/_navigation.html.erb <nav class="navbar navbar-inverse"> <div class="container"> <div class="navbar-header"> <%= link_to 'Tutsplus Devise', root_path, class: 'navbar-brand' %> </div> <div id="navbar"> <ul class="nav navbar-nav"> <li><%= link_to 'Home', root_path %></li> </ul> <ul class="nav navbar-nav pull-right"> <% if user_signed_in? %> <li class="dropdown"> <a class="dropdown-toggle" data-toggle="dropdown" href="#"> <%= current_user.username %> <span class="caret"></span> </a> <ul class="dropdown-menu" role="menu"> <li><%= link_to 'Invite', new_user_invitation_path %></li> <li><%= link_to 'Profile', edit_user_registration_path %></li> <li><%= link_to 'Log out', destroy_user_session_path, method: :delete %></li> </ul> </li> <% else %> <li><%= link_to 'Log In', new_user_session_path %></li> <li><%= link_to 'Sign Up', new_user_registration_path %></li> <% end %> </ul> </div> </div> </nav>
ดู routes ของ DeviseInvitable: rake routes | invit output:
cancel_user_registration GET /users/cancel(.:format) devise_invitable/registrations#cancel user_registration POST /users(.:format) devise_invitable/registrations#create new_user_registration GET /users/sign_up(.:format) devise_invitable/registrations#new edit_user_registration GET /users/edit(.:format) devise_invitable/registrations#edit PATCH /users(.:format) devise_invitable/registrations#update PUT /users(.:format) devise_invitable/registrations#update DELETE /users(.:format) devise_invitable/registrations#destroy accept_user_invitation GET /users/invitation/accept(.:format) devise/invitations#edit remove_user_invitation GET /users/invitation/remove(.:format) devise/invitations#destroy user_invitation POST /users/invitation(.:format) devise/invitations#create new_user_invitation GET /users/invitation/new(.:format) devise/invitations#new PATCH /users/invitation(.:format) devise/invitations#update PUT /users/invitation(.:format) devise/invitations#update
ลองดูตอนนี้ รัน server rails server ไปที่ https://localhost:3000/users/invitation/new กรอก email submit ควรทำงาน! ใน server logs จะเห็น link accept invite
ดีกว่าถ้าดู email ใน browser ได้ มาดูวิธีทำ
Integrating Letter_Opener
Letter Opener preview emails ใน browser default ไม่ต้อง setup mail delivery system ใน development
Gemfile เพิ่ม:
gem 'letter_opener'
รัน bundle install
แก้ config/environments/development.rb เพิ่ม:
#config/environments/development.rb ... config.action_mailer.delivery_method = :letter_opener end
Restart server ไป https://localhost:3000/users/invitation/new กรอก submit คราวนี้หน้าต่างใหม่ pop up แสดง invite email
Change Default Sign In and Sign Out Routes
Default sign_in และ sign_out routes ของ Devise:
sign_in: https://localhost:3000/users/sign_in
sign_out: https://localhost:3000/users/sign_out
แก้ใน config/routes.rb เพิ่ม:
#config/routes.rb as :user do get 'signin' => 'devise/sessions#new' post 'signin' => 'devise/sessions#create' delete 'signout' => 'devise/sessions#destroy' end
ไป https://localhost:3000/signin ได้แล้ว
Conclusion
ตอนนี้รู้ใช้ DeviseInvitable แล้ว ยังรู้ gem letter_opener มีอะไรทำกับ Devise เยอะ เช็ค Wiki เรียนรู้เพิ่ม ขอบคุณที่ติดตาม