Привет.
Сегодня посмотрим как работать с devise и cancan. Важные gem'ы, да?
и так сперва задача:
- Авторизация\регистрация\аутентификация пользователя на сайте -- это задача devise
- Потом привилегии:
- админ может всё ;)
- пользователь может создавать посты
- автор постов (владелец) может его редактировать, но удалять не может.
Не очень сложно.
и так в Gemfile добавляем
gem 'devise'
gem 'cancan'
Ok, установили теперь devise:
rails g devise:installЭта команда создаст файл инициализации, нам нужно его отредактировать:
config/initializers/devise.rb
находим строчку
config.sign_out_via = :delete
меняем на :get.
Так хорошо. Теперь делаем модель пользователя:
rails g devise userСоздаёт модель. Ну и сразу делаем миграцию, что создаёт таблицу в БД:
rake db:migrateимпортируем views
rails g devise:viewsОк. Теперь делаем для постов, тут легче:
rails g scaffold Post title:string content:text user:references rake db:migrateВсё! Что хотели, то сделали: можно регистрироваться и все дела.
Теперь время добавить привилегии с cancan:
rails g cancan:abilityСоздаёт класс возможностей\привилегий в app/models.
Ну в принципе каркас написан, осталось добавить немного кода.
Но перед этим нужны же ещё Роли (Role). У пользователя может быть тысяча ролей, и модератор и просто_пользователь в общем придумайте сами.
У меня -- это будет admin\user и всё. и так как видно это отношение has_many:through
rails g model role name:string rails g model users_role user:references role:references
Всё теперь редактируем модель User, добавляем зависимости:
class User < ActiveRecord::Base has_many :users_roles has_many :roles, :through => :users_roles end
Модель Role
class Role < ActiveRecord::Base has_many :users_roles has_many :users, :through => :users_roles end
Всего лишь строчку в PostController:
load_and_authorize_resourceи всё.
Теперь с представлением:
Ну допустим раньше выводилось вот так:
#views/index.html.erb <% @posts.each do |post| %> <tr> <td><%= post.title %></td> <td><%= post.content %></td> <td><%= post.user %></td> <td><%= link_to 'Show', post %></td> <td><%= link_to 'Edit', edit_post_path(post) %></td> <td><%= link_to 'Destroy', post, confirm: 'Are you sure?', method: :delete %></td> </tr> <% end %> <br /> <%= link_to 'New Post', new_post_path %>Теперь с помощью волшебного метода can делаем:
<h1>Listing posts</h1> <table> <tr> <th>Title</th> <th>Content</th> <th>User</th> <th></th> <th></th> <th></th> </tr> <% @posts.each do |post| %> <tr> <td><%= post.title %></td> <td><%= post.content %></td> <td><%= post.user %></td> <% if can? :read, post %> <td><%= link_to 'Show', post %></td> <% end %> <% if can? :update, post %> <td><%= link_to 'Edit', edit_post_path(post) %></td> <% end %> <% if can? :destroy, post %> <td><%= link_to 'Destroy', post, confirm: 'Are you sure?', method: :delete %></td> <% end %> </tr> <% end %> </table> <% if can? :create, Post %> <%= link_to 'New Post', new_post_path %> <% end %>Также не забывайте в контроллере PostsController в методе create устанавливать связь пользователя и нового поста:
def create @post = current_user.posts.new(params[:post]) .... end
Потом напишем немного больше и продолжим с cancan + добавим спеки (rspec)
Немного UPD
Чтобы сделать меню, где вход\выход -- стандратное меню, нужно например в views/layouts/application.html.erb
добавить что-то вроде этого:
<div id="header"> <%= render "shared/links" %> </div>Папку shared создайте сами, и в ней файл _links.html.erb
<ul> <% if user_signed_in? %> <li><%= link_to "My Profile", edit_user_registration_path%> </li> <li><%= link_to "Sign out", destroy_user_session_path%> </li> <% else %> <li><%= link_to "Sign up", new_user_registration_path%> </li> <li><%= link_to "Sign in", new_user_session_path%> </li> <% end %> </ul>
Потом для правильно отрисовки в app/helpers/application_helper.rb:
module ApplicationHelper def resource_name :user end def resource @resource ||= User.new end def devise_mapping @devise_mapping ||= Devise.mappings[:user] end end
В модели User:
#models/user.rb class User < ActiveRecord::Base before_create :create_role has_many :posts #надеюсь вы не забыли установить эту зависимость :) private def create_role self.roles << Role.find_by_name(:user) end end
Ещё UPDКогда мы ищём роль: Role.find_by_name(:user), то предпологается, что уже есть такая роль. Поэтому перед запуском приложения в файле db/seeds.rb
Role.create(:name => :admin) Role.create(:name => :user)И потом в консоли rake db:seed
UPD код: http://code.google.com/p/example-app/
Прошлый пост