ADZ 學習筆記

Ruby/Rails, Startup, Life

rails 筆記 - 封閉會員網站 scaffold controller & devise

| Comments

最近案子的需求是一個封閉系統,只能由有 admin 權限的 user 才能創造 user,首先設定 router

routes.rb
devise_for :users, :skip => [:sessions, :password, :registrations] do
  get "/login" => "users/sessions#new", :as => :new_user_session
  get "/logout" => "devise/sessions#destroy", :as => :destory_user_session
  post "/do-login" => "users/sessions#create", :as => :user_session
end

突然想到,既然關閉 registrations controller,那我要怎麼創造第一個 user ... !? user table 內的密碼不是加密過的嗎? 那 devise 的加密 funnction 在哪? google 了一下看到有人用 rails c 的方式

rails c
new_user = User.new
new_user.email = 'xxx@domain.com'
new_user.password = 'password'
new_user.password_confirmation = 'password'
new_new.save

比想像中更簡單直覺,果然學新東西,必須先把思維拋掉!!

但奇怪的一點是 User 沒有 password & password_confirmation 欄位,User Model 也只 extend ActiveRecord::Base,但裝好 devise 後,突然跑出這兩個欄位,不知道是透過什麼方式 inject 這些 property 到 user model 裡面去的,還實做好 password_confirmation 的檢查,只能說這真是太神奇了!!!

成功創造第一個 user 後,順便寫在 seeds.rb 裡

seeds.rb
if User.where({username: 'admin'}).count == 0
  User.create!(:username => 'admin', :password => 'password', :'password_confirmation' => 'password')
end

接下來是新增會員的功能,原本想說直接讓管理者透過 devise registrations controller 來新增會員,但看到一篇 github上的討論 以後,還是用決定用 scaffold,查一下有沒有可以 skip-model 之類的 option,結果直接看到 rails g 裡有一個項目 scaffold_controller

Description:
    Stubs out a scaffolded controller, its seven RESTful actions and related
    views. Pass the model name, either CamelCased or under_scored. The
    controller name is retrieved as a pluralized version of the model name.

就是這個,直接 ..

rails g scaffold_controller admin/user

建好後

routers.rb
namespace :admin do
    resources :users
end
admin/users_controller.rb
# GET /admin/users/1

def index
    @admin_users = Admin::User.all
end

# GET /admin/users/new

def new
    @admin_user = Admin::User.new
end
# ... (略)

發現在 admin/users_controller.rb 都是使用 Admin::User,但實際上要操作的 model 並不是在 admin/ 內,於是就把全部 Admin:: 拿掉,然後開啓頁面後 error ... user_path undefinded

打開 view

admin/users/index.html.erb
<tr>
    <td><%= link_to 'Show', admin_user %></td>
    <td><%= link_to 'Edit', edit_admin_user_path(admin_user) %></td>
    <td><%= link_to 'Destroy', admin_user, method: :delete, data: { confirm: 'Are you sure?' } %></td>
</tr>

本來有點不解為什麼 Show & Destroy 的 link_to 第二個參數可以直接把 admin_user 帶入,之後想想,也許這種寫法會直接使用當前 activerecordmodel 名稱產生對應的 view helper,如果是這樣,那這種設計真的很 smart,現在稍微體會 rails Convention over configuration 所帶來的效率,也開始覺得 rails 應該不是 for 剛接觸程式語言的人 ...

修改後的 view

admin/users/index.html.erb
<!-- .. (略) -->
<%= link_to 'Show', admin_user_path(admin_user.id) %>
<%= link_to 'Destroy', admin_user_path(admin_user.id), method: :delete, data: { confirm: 'Are you sure?' } %>
<!-- .. (略) -->

成功 :)

之後在編輯和新增的畫面出現錯誤,看來 form_for 也有類似問題

admin/users/_form.html.erb
<%= form_for(@admin_user) do |f| %>
<!-- ... (略) -->
<% end %>

這次解法就不是改 path helper

admin/users/_form.html.erb
<%= form_for(['amdin', @admin_user]) do |f| %>
<!-- ... (略) -->
<% end %>

其實我也看不太懂這是什麼用法,也許是指定 namspace admin 底下的 resources path 吧!! 總而言之,CRUD 都正常了,剩下最後一步 ... Admin::UsersController 加上 is_admin 檢查

admin/users_controller.rb
class Admin::UsersController < ApplicationController
    before_filter :admin_checker
  protected
  def admin_checker
    if current_user.is_admin? == false
        flash[:notice] = 'access denied'
      redirect_to root_path
    end
  end
    #... (略)

end

收工 :)

Comments

comments powered by Disqus