Привет.
Продолжим с devise и cancan.
Что будем делать?
Допустим я хочу сделать так чтобы даже не вошедший пользователь мог бы сделать пост, нет
он его не опубликует, просто его перекинет на страницу входа. Вот.
Для этого делаем контроллер таким:
Метод :authenticate_user! он определён в devise.
Что он делает?
Если пользователь вошёл, то ничего (на самом деле он проверяет)
Если не произведён вход на сайт, то будет перенаправлен (redirect_to) на страницу входа /sign_in
Далее стандартная load_and_authorize_resource из cancan
Затем снова метод из cancan skip_authorize_resource :only => :new который сбрасывает проверку привилегий для
метода (:new).
Вот достаточно просто.
Ах да ещё убираем из view нашу проверку
Что дальше?
Для системы, которую делаю, неплохо бы добавить к постам комментарии, это же оживляет страницу)
Создаём модель\контроллер\и сразу миграцию
В класс модели User добавляем след. строчку:
В модель Post
Ну и далее нужная нам новая модель Comment
С validates понятно у комментария должен быть пользователь\пост\ну и собственно сам текст комментария.
attr_accessible -- (та строчка, из-за которой была паника на GitHub) указывает на то, что можно изменять только это поле (:body), но
не пользователя и пост.
Затем добавляем роутинг.
То есть навигация по комментариям идёт уже в определённом посте.
Вот часть сделали. Теперь контроллер
Также видна строчка из cancan load_and_authorize_resource, которую я добавил (а значит изменил и models/ability.rb)
Потом важная вещь переопределил исключение, когда действие не допустимо. Если помните такое же переопределение есть в
ApplicationController так вот там это исключение, если пользователь делает что-то в не своих правах, то будет переброшен
на root_url, а здесь просто возврашаем на тот же пост. Вот и всё.
Методы также просты и не должны вызывать непонимания (чуть что в комментариях задайте вопрос)
Так теперь Ability
Теперь с view.
Форма для создания комментария
Затем для вывода комментариев для поста:
rake routes
то покажется куча роутов среди которых есть и post_comment_path, для которого нужен пост и коммент, что мы и передаём ему, а rails сам формирует правильную ссылку.
Теперь добавляем наши view в вывод поста:
Ну и теперь куча тестов:
Сначала для PostsController, там же добавили некоторые вещи.
Ну и для CommentsController
Продолжим с devise и cancan.
Что будем делать?
Допустим я хочу сделать так чтобы даже не вошедший пользователь мог бы сделать пост, нет
он его не опубликует, просто его перекинет на страницу входа. Вот.
Для этого делаем контроллер таким:
#controllers/post_controller.rb class PostsController < ApplicationController before_filter :authenticate_user!, :only => [:new] load_and_authorize_resource skip_authorize_resource :only => :new #... endКак видно тут добавлен before_filter и метод :authenticate_user!, который только для действия new.
Метод :authenticate_user! он определён в devise.
Что он делает?
Если пользователь вошёл, то ничего (на самом деле он проверяет)
Если не произведён вход на сайт, то будет перенаправлен (redirect_to) на страницу входа /sign_in
Далее стандартная load_and_authorize_resource из cancan
Затем снова метод из cancan skip_authorize_resource :only => :new который сбрасывает проверку привилегий для
метода (:new).
Вот достаточно просто.
Ах да ещё убираем из view нашу проверку
#views/posts/index.html.erb <%= link_to 'New Post', new_post_path %>убрали can? перед ссылкой на создание нового поста.
Что дальше?
Для системы, которую делаю, неплохо бы добавить к постам комментарии, это же оживляет страницу)
Создаём модель\контроллер\и сразу миграцию
rails g model comment post:references user:references body:text rails g controller comments rake db:migrateХоп! Всё сделано, остался код + тесты
В класс модели User добавляем след. строчку:
#models/user.rb class User < ActiveRecord::Base # ... has_many :comments, :dependent => :destroy endЭтот макрос отношение один-ко-многим, то есть у одного пользователя может быть сотня-другая комментраиев.
В модель Post
#models/post.rb class Post < ActiveRecord::Base #... has_many :comments, :dependent => :destroy endТо же что и для User у одного поста много-много комментариев, с удалением поста все они тоже будут удалены.
Ну и далее нужная нам новая модель Comment
#models/comment.rb class Comment < ActiveRecord::Base belongs_to :post belongs_to :user validates :user_id, :post_id, :body, :presence => true attr_accessible :body endПервые методы belongs_to указываеют на связь к таблицам posts\users.
С validates понятно у комментария должен быть пользователь\пост\ну и собственно сам текст комментария.
attr_accessible -- (та строчка, из-за которой была паника на GitHub) указывает на то, что можно изменять только это поле (:body), но
не пользователя и пост.
Затем добавляем роутинг.
#/config/routes.rb resources :posts do resources :comments, :only => [:create, :destroy] endТут вложенный ресурс comments ссылки будут выглядеть posts/post_id/comments/
То есть навигация по комментариям идёт уже в определённом посте.
Вот часть сделали. Теперь контроллер
#controllers/comments_controller.rb class CommentsController < ApplicationController load_and_authorize_resource def create @post = Post.find(params[:post_id]) comment = @post.comments.build(params[:comment]) comment.user = current_user if comment.save flash[:notice] = 'Comment was successfully created.' redirect_to post_path(@post) else flash[:notice] = 'The comment you typed was invalid.' render "posts/show" end end def destroy post = Post.find([params[:post_id]]) comment = Comment.find(params[:id]) comment.delete flash[:notice] = "Delete" redirect_to post_path(post) end rescue_from CanCan::AccessDenied do |exception| flash[:notice] = "You can not create or delete comment" redirect_to post_path(Post.find(params[:post_id])) end endВ контроллере есть методы destroy, который удаляет комментарий и метод create, который создаёт комментарий.
Также видна строчка из cancan load_and_authorize_resource, которую я добавил (а значит изменил и models/ability.rb)
Потом важная вещь переопределил исключение, когда действие не допустимо. Если помните такое же переопределение есть в
ApplicationController так вот там это исключение, если пользователь делает что-то в не своих правах, то будет переброшен
на root_url, а здесь просто возврашаем на тот же пост. Вот и всё.
Методы также просты и не должны вызывать непонимания (чуть что в комментариях задайте вопрос)
Так теперь Ability
#models/ability.rb class Ability #... def initialize(user) #... if user.role? :admin #... can [:create, :destroy], Comment else if user.role?(:user) can :create, Comment #... end end endПоказал только то, что добавил. Обычный пользователь может комментировать, а админ плюс ко всему и удалять неугодные комментарии)))
Теперь с view.
Форма для создания комментария
#views/comments/_form.html.erb <% if can? :create, Comment %> <%= form_for [@post, current_user.comments.new] do |f| %> <%= f.text_area :body, :size => "70%x10" %> <br/> <%= f.submit "add comment" %> <% end %> <% end %>Снова метод can? который проверяет может ли пользователь создавать комментарий. Ну и потом стандартная форма.
Затем для вывода комментариев для поста:
#views/comments/_index.html.erb <% @post.comments.each do |comment| %> <p> <b> Name:<%= comment.user.email %> </b> <%= comment.body %> <% if can? :clear, Vote %> <%= link_to 'delete', post_comment_path(@post, comment), method: :delete %> <% end %> </p> <% end %>Тут просто выводим email пользователья (потом улучшим будет имя) ну и сам комментарий. А также ссылка для админа, который может удалить комментарий. Формирование ссылки посмотрите, как строится: если мы наберём
rake routes
то покажется куча роутов среди которых есть и post_comment_path, для которого нужен пост и коммент, что мы и передаём ему, а rails сам формирует правильную ссылку.
Теперь добавляем наши view в вывод поста:
#views/posts/show.html.erb <%= render 'comments/index'%> <%= render 'comments/form' %>Просто добавте пару строчек и всё (после вывода поста конечно же, но можете и перед, как угодно).
Ну и теперь куча тестов:
Сначала для PostsController, там же добавили некоторые вещи.
#spec/controllers/post_controller_spec.rb
describe "GET new" do
before(:each) {
@user = Factory(:user)
@user.roles << Factory(:role)
sign_in @user
}
it "asigns a new post as @post" do
get 'new'
assigns(:post).should be_a_new(Post)
response.should render_template('posts/new')
end
it "asigns a new post as @post if User auth" do
get 'new'
assigns(:post).should be_a_new(Post)
response.should render_template('posts/new')
end
it "should redirect_to if user not auth" do
sign_out @user
get 'new'
response.should redirect_to(new_user_session_path)
end
end
Надеюсь названия понятны и говорят сами за себя.Ну и для CommentsController
/spec/controllers/comments_controller_spec.rb require 'spec_helper' describe CommentsController do describe "POST create" do before(:each) { @user = Factory(:user) @user.roles << Factory(:role) @post = Factory(:post, :user => @user) } it "should create new comment if user can create comments" do sign_in @user attr = {:body => "new comment"} post 'create', {:post_id => @post.id, :comment => attr } flash[:notice].should eq('Comment was successfully created.') response.should redirect_to(post_path(@post)) end it "should render posts/show if attr is invalid" do sign_in @user post 'create', {:post_id => @post.id, :comment => {}} flash[:notice].should eq('The comment you typed was invalid.') response.should render_template('posts/show') end it "should not create new comment if user can not ability" do post 'create', {:post_id => @post.id, :comment => {}} flash[:notice].should eq("You can not create or delete comment") response.should redirect_to(post_path(@post)) end end describe "DELETE destroy" do before(:each) { @post = Factory(:post, :user => Factory(:user)) comment = Factory(:comment, :user => Factory(:user), :post => @post) } it "should delete comment if admin" do user = Factory(:user) user.roles << Factory(:role, :name => :admin) sign_in user @post.comments.count.should eq(1) delete 'destroy', { :post_id => @post.id, :id => @post.comments.last.id } flash[:notice].should eq("Delete") response.should redirect_to(post_path(@post)) @post.comments.count.should eq(0) end it "should not delete comment if user" do user = Factory(:user) user.roles << Factory(:role) sign_in user @post.comments.count.should eq(1) delete 'destroy', { :post_id => @post.id, :id => @post.comments.last.id } flash[:notice].should eq("You can not create or delete comment") response.should redirect_to(post_path(@post)) @post.comments.count.should eq(1) end end end
Спасибо за сбор инфы в одном мете =) давно искал
ОтветитьУдалитьP.S.
skip_authorize_resource :only => :new ( можно вроде как убрать - я чет не увил что он даёт - так как, если ты авторизован ты в любом случае можешь создавать посты, а если нет - до skip-a управление не дойдет ( так как devise перехватит и отредеректит на страницу с логином )