Popular Posts

Показаны сообщения с ярлыком example. Показать все сообщения
Показаны сообщения с ярлыком example. Показать все сообщения

понедельник, 5 марта 2012 г.

Devise + Cancan -- быстрая разработка


Привет.
Сегодня посмотрим как работать с 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

Ok, всё круто, но я чувствую у вас вопрос: Откуда взять роль, когда пользователь зарегистрировался? Ответ -- callbacks
В модели 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/


Прошлый пост