Статьи

Создание форума с нуля с помощью Ruby on Rails

Сегодня мы будем создавать простой форум с использованием Ruby on Rails, и мы будем работать над основами, охватывающими такие вещи, как аутентификация и более продвинутые методы работы с базами данных.

Конечный продукт

Если вы не знакомы с Ruby on Rails, это платформа веб-приложений, созданная с использованием языка программирования Ruby. На net.tuts + было опубликовано несколько хороших руководств по началу работы с Rails, и для быстрого обзора вы можете посмотреть мою предыдущую статью .

Чтобы создать наше приложение, мы используем команду rails, за которой следует имя нашего проекта, и в данном случае это форум. Команда терминала ниже — то, что я использовал. Кроме того, убедитесь, что вы перешли в правильный каталог в вашем терминале.

1
rails forum

После выполнения команды вы увидите длинную строку текста со всеми файлами, которые только что были созданы. Теперь вы захотите перейти в каталог, который мы только что создали.

Вам, наверное, интересно, что такое Драгоценный камень. Gem — это автономный пакет для распространения программ и библиотек Ruby. Они очень часто пригодятся, они облегчают вашу жизнь. Для этого проекта мы будем использовать несколько драгоценных камней, перечисленных ниже. Также убедитесь, что у вас установлена ​​последняя версия RubyGems и ваших Gems, запустив:

1
gem update —system

(с sudo, если нужно)

1
gem update

(с sudo, если нужно)

  • изящные генераторы: удивительные изящные генераторы Райана Бейтса будут использоваться для генераторов изящной аутентификации и изящных скаффолдов
  • база данных: убедитесь, что у вас есть любой гем, необходимый для взаимодействия с базой данных, которую вы будете использовать, я собираюсь использовать SQLite

Теперь, когда у нас есть все гемы, которые нам нужны, запустите на своем терминале следующее: (убедитесь, что вы находитесь в созданных директориях)

1
script/generate nifty_authentication

Вы должны увидеть, как текст пролетает как выше. Теперь откройте папку в вашей любимой IDE. Сначала мы отредактируем файл config / database.yml. Файл должен быть настроен на использование любой системы баз данных, которую вы хотите, и в этом случае я буду использовать SQLite.

Убедитесь, что все настроено правильно, и затем запустите следующую команду, чтобы перенести изменения в нашу базу данных:

1
rake db:migrate

Нет, эта часть фактически не будет касаться макета приложения с точки зрения общего дизайна, мы скоро над этим поработаем. Этот раздел на самом деле будет посвящен тому, как будет построено приложение. Одним из главных преимуществ Ruby on Rails является множество доступных генераторов. Единственная проблема, которую пропускают некоторые люди, заключается в том, что вам нужно спланировать свое приложение, чтобы эти генераторы действительно прошли лишнюю милю.

Ниже показано, как модели в базе данных будут организованы и связаны грубым образом:

  • Форум: Форум — это самый высокий уровень. В основном группа тем, которые связаны друг с другом.
    • Тема: тема, также называемая веткой, относится к определенной теме.
      • Сообщение: сообщение относится к теме и является сообщением определенного пользователя.

Таким образом, мы собираемся использовать отличный механизм для создания трех моделей, по одной для каждой из частей, перечисленных выше. Итак, для начала, мы собираемся использовать эшафот для создания нашего форума. Для этого выполните следующую команду:

1
script/generate nifty_scaffold forum name:string description:text

Далее будем строить леску по теме:

1
script/generate nifty_scaffold topic name:string last_poster_id:integer last_post_at:datetime

Теперь мы будем ремонтировать пост:

1
script/generate nifty_scaffold post content:text

Затем перенесите базу данных:

1
rake db:migrate

Теперь мы собираемся поработать над первой частью общего вида сайта. В связи с тем, что мы используем nifty_scaffold, мы также собираемся использовать nifty_layout для генерации некоторого базового макета, который мы изменим. Чтобы использовать nifty_layout, запустите:

1
script/generate nifty_layout

Теперь это был действительно быстрый шаг, чтобы заставить это работать на данный момент. Мы вернемся к этому позже.

Итак, мы собираемся создавать ассоциации в наших моделях. Ассоциации — это способы связать модели. Давайте сначала откроем наше приложение / models / forum.rb, и вы должны увидеть:

1
2
3
# app/models/forum.rb
class Forum < ActiveRecord::Base
end

Теперь мы собираемся добавить некоторый код, и он должен выглядеть следующим образом, и я объясню это через минуту.

1
2
3
4
# app/models/forum.rb
class Forum < ActiveRecord::Base
  has_many :topics, :dependent => :destroy
end

Код has_many означает именно то, на что это похоже; у него «много», а затем мы ссылаемся на нашу модельную тему. Вы можете удивиться, почему это работает, когда вы добавляете ‘s’, и это соглашение ActiveRecord. Значение: зависимый =>: уничтожить означает, что когда форум будет удален, все его темы будут тоже. Теперь мы собираемся открыть наш app / models / topic.rb и отредактировать его следующим образом:

1
2
3
4
5
# app/models/topic.rb
class Topic < ActiveRecord::Base
  belongs_to :forum
  has_many :posts, :dependent => :destroy
end

Опять же, это простой текст. В нем говорится, что тема принадлежит форуму и имеет много сообщений. Мы также используем: зависимый =>: уничтожить здесь, чтобы удалить все его дочерние сообщения. Теперь откройте app / models / post.rb и отредактируйте его следующим образом:

1
2
3
4
# app/models/post.rb
class Post < ActiveRecord::Base
  belongs_to :topic
end

Теперь мы собираемся создать три миграции, которые добавят наши внешние ключи в нашу базу данных. Если бы вы хотели сделать это, когда вы строили леса, вы могли бы это сделать, хотя то, как мы это делаем, также учит вас другой технике Rails, поэтому я чувствовал, что этот путь будет лучше. Итак, создайте нашу первую миграцию, запустите:

1
script/generate migration add_foreign_to_topics forum_id:integer

Эта команда генерирует миграцию с именем add_foreign_to_topics, автоматически распознает, что мы хотим изменить модель Topic, и затем мы передаем то, что хотим добавить. В этом случае мы добавляем столбец с именем forum_id, который является целочисленным типом. Чтобы создать миграцию, добавьте наш внешний ключ forum_id в нашу модель Topic. Затем следующая команда создаст миграцию

1
script/generate migration add_foreign_to_posts topic_id:integer

Теперь мы можем перенести базу данных снова:

1
rake db:migrate

Итак, теперь мы можем начать строить функциональность сайта. Итак, давайте сначала откроем config / rout.rb и добавим следующую строку рядом с закомментированными строками ниже:

1
2
# config/routes.rb
map.root :controller => «forums»

Позволяет сохранить файл и удалить или переименовать файл public / index.html. Теперь мы хотим открыть представление для файла индекса в app / views / forums / index.html.erb и отредактировать его следующим образом:

01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
16
17
18
19
20
21
22
# app/views/forums/index.html.erb
<% title «Forums» %>
 
<table>
  <tr>
    <th width=»70%»>Forum</th>
    <th width=»30%»>Last Post</th>
  </tr>
  <% for forum in @forums %>
    <tr>
      <td><h4><%= link_to h(forum.name), forum_path(forum.id) %></h4>
        <small><%= forum.topics.count %> topics</small><br />
        <%=h forum.description %></td>
      <td class=»right»></td>
      <td><%= link_to «Show», forum %></td>
      <td><%= link_to «Edit», edit_forum_path(forum) %></td>
      <td><%= link_to «Destroy», forum, :confirm => ‘Are you sure?’, :method => :delete %></td>
    </tr>
  <% end %>
</table>
 
<p><%= link_to «New Forum», new_forum_path %></p>

Я изменил текст заголовка таблицы на текст, который мы будем помещать в каждую ячейку, а также ширину ячеек. Есть еще изменения, которые мы собираемся внести позже. Теперь откройте файл application.css в каталоге public / stylesheets. Я добавил файлы CSS Reset и Typography 960.gs в начало таблицы стилей. Затем я добавил следующие стили в конец файла:

1
2
3
4
5
# public/stylesheets/application.css
table tr th {background:#222;
table tr td {border:1px #ccc solid;
table tr td.right {background: #eee;}
table tr td h4 {margin:0;

Теперь, если вы хотите запустить сервер с помощью скрипта / сервера, вы должны увидеть следующее: (PS Я добавил запись в базу данных только для примера.)

Теперь, когда мы позаботились о нашем действии по индексам, давайте поработаем над нашим показательным действием для форума. Этот файл находится в app / views / forums / show.html.erb. Мы собираемся скопировать большую часть кода из нашего действия индекса в этот, и окончательный код ниже:

01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
16
17
18
19
20
21
22
# app/views/forums/show.html.erb
<% title @forum.name %>
 
<table>
  <tr>
    <th width=»60%»>Topic Title</th>
    <th width=»10%»>Replies</th>
    <th width=»30%»>Last Post</th>
  </tr>
  <% for topic in @forum.topics %>
    <tr>
      <td><%= link_to h(topic.name), topic_path(topic.id) %>
      <td><%= topic.posts — 1 %></td>
      <td class=»right»></td>
      <td><%= link_to «Show», topic %></td>
      <td><%= link_to «Edit», edit_topic_path(topic) %></td>
      <td><%= link_to «Destroy», topic, :confirm => ‘Are you sure?’, :method => :delete %></td>
    </tr>
  <% end %>
</table>
 
<p><%= link_to «New Topic», «/topics/new?forum=#{@forum.id}» %></p>

Я знаю, что я не объяснил код в двух представлений выше, поэтому я объясню это сейчас. Итак, первой строкой мы устанавливаем заголовок страницы. Затем мы создаем таблицу и настраиваем заголовки таблицы. После этого мы запускаем цикл для каждого элемента в темах нашего форума и используем локальную переменную theme. Затем мы создаем ссылку для каждой темы, затем количество сообщений минус 1 для учета исходного сообщения. Затем у нас есть дополнительный тд, который мы будем использовать позже, а затем ссылки администратора, о которых мы также позаботимся позже.

Итак, теперь у нас есть базовый макет нашего интерфейса. Нам нужно поработать над формой, чтобы создать тему. У вас должно быть что-то похожее на следующее:

Ну, это в настоящее время не работает для того, что мы хотим сделать. В настоящее время мы не создаем сообщение при создании темы, и в настоящее время мы предоставляем пользователям доступ к параметрам, которые они не должны видеть. Так что откройте app / views / themes / _form.html.erb. Ваш текущий файл должен выглядеть так:

01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
16
17
# app/views/topics/_form.html.erb
<% form_for @topic do |f|
  <%= f.error_messages %>
  <p>
    <%= f.label :name %><br />
    <%= f.text_field :name %>
  </p>
  <p>
    <%= f.label :last_poster_id %><br />
    <%= f.text_field :last_poster_id %>
  </p>
  <p>
    <%= f.label :last_post_at %><br />
    <%= f.datetime_select :last_post_at %>
  </p>
  <p><%= f.submit «Submit» %></p>
<% end %>

Итак, мы собираемся удалить части с last_poster_id и last_post_at. Мы также собираемся добавить в текстовую область для сообщения и некоторые скрытые поля. Вы увидите, что я не пользуюсь помощниками Rails, и это мое личное предпочтение. Ваша окончательная форма должна выглядеть так:

01
02
03
04
05
06
07
08
09
10
11
12
13
# app/views/topics/_form.html.erb
<% form_for @topic do |f|
  <%= f.error_messages %>
  <% if params[:forum] %><input type=»hidden» id=»topic_forum_id» name=»topic[forum_id]» value=»<%= params[:forum] %>» /><% end %>
  <p>
    <%= f.label :name %><br />
    <%= f.text_field :name %>
  </p>
  <p>
    <textarea name=»post[content]» cols=»80″ rows=»20″><%= @post.content %></textarea>
  </p>
  <p><%= f.submit «Create» %></p>
<% end %>

И это должно выглядеть так в вашем браузере:

Мы будем сохранять их в нашем контроллере. Итак, сохраните представление и откройте app / controllers / topic_controller.rb и найдите действие create. Отредактируйте его так, чтобы оно выглядело так:

01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
16
# app/controllers/topic_controller.rb
def create
  @topic = Topic.new(params[:topic])
  if @topic.save
    @topic = Topic.new(:name => params[:topic][:name], :last_poster_id => current_user.id, :last_post_at => Time.now, :forum_id => params[:topic][:forum_id])
 
    if @post.save
      flash[:notice] = «Successfully created topic.»
      redirect_to «/forums/#{@topic.forum_id}»
    else
      redirect :action => ‘new’
    end
  else
    render :action => ‘new’
  end
end

Мы добавили в код, чтобы обрабатывать создание сообщения, которое идет по теме. Теперь, чтобы сделать это проще, мы собираемся удалить действие index в нашем контроллере. Мы также собираемся обновить обновления и уничтожить действия для правильного перенаправления, поэтому измените redirect_to для этих двух действий на:

1
redirect_to «/forums/#{@topic.forum_id}»

Теперь откройте app / controllers / posts_controller.rb, удалите индекс и покажите действия.

Если вы заметили, что я пропустил две ассоциации выше, я приветствую вас, заметив. Мы пропустили ассоциации между пользователями, их сообщениями и темами. Для этого сначала нужно создать две миграции и перенести изменения в базу данных:

1
2
3
script/generate migration add_user_to_posts user_id:integer
script/generate migration add_user_to_topics user_id:integer
rake db:migrate

Теперь, чтобы сообщить ActiveRecord об этих ассоциациях, откройте app / models / user.rb и добавьте следующие строки в before_save.

1
2
3
# app/models/user.rb
has_many :posts
has_many :topics

Теперь откройте app / models / post.rb и app / models / topic.rb и добавьте следующие строки:

1
2
# app/models/post.rb & app/models/topic.rb
belongs_to :user

Теперь у нас есть дополнительные ассоциации, чтобы продолжить.

Итак, теперь, когда у нас есть основы, мы продолжим и откорректируем список форумов, после чего продолжим и отшлифуем список тем.

Давайте сначала откроем приложение / models / forum.rb. Мы собираемся добавить новый метод, чтобы помочь нам найти самую последнюю запись в форуме. Метод, который мы собираемся добавить, ниже:

1
2
3
4
5
# app/models/forum.rb
def most_recent_post
  topic = Topic.first(:order => ‘last_post_at DESC’, :conditions => [‘forum_id = ?’, self.id])
  return topic
end

Этот метод находит первую тему, у которой есть идентификатор форума независимо от идентификатора форума, для которого мы вызвали этот метод, а затем сортирует его по дате и возвращает значение.

Так что откройте app / views / forums / index.html.erb и найдите ячейку таблицы внутри цикла, которая имеет класс права. Мы собираемся изменить это на следующее:

1
2
# app/views/forums/index.html.erb
<td class=»right»><% if forum.most_recent_post %><%= distance_of_time_in_words_to_now forum.most_recent_post.last_post_at %> ago<% else %>no posts<% end %></td>

Этот код может выглядеть запутанным, поэтому я пройдусь по нему. Сначала мы ссылаемся на метод, который мы добавили в нашу модель, и посмотрим, вернула ли она запись, потому что у нас может быть форум без каких-либо тем. Далее, если у нас есть запись, мы используем помощников по дате, которые Rails предоставляет для разбора времени на читаемый текст. Если у нас нет возвращенной записи, мы выводим ‘no posts’. Ваша страница должна выглядеть так:

Теперь, когда мы добавили дату, мы собираемся добавить в ссылку на пользователя, который разместил. Вернувшись в представление, измените эту ячейку таблицы следующим образом:

1
2
3
4
5
6
# app/views/forums/index.html.erb
<td class=»right»><% if forum.most_recent_post %>
  <%= distance_of_time_in_words_to_now forum.most_recent_post.last_post_at %> ago by
  <%= link_to forum.most_recent_post.user.username, «/users/#{forum.most_recent_post.last_poster_id}» %>
  <% else %>no posts<% end %>
</td>

Ваша страница должна теперь выглядеть так:

Итак, теперь мы вернемся к нашему файлу app / views / forums / show.html.erb. Название этого шага может быть немного запутанным из-за того, что мы редактируем действие шоу на форуме, но фактически работаем над списком тем. Теперь, когда у вас есть открытый файл, снова найдите ячейку таблицы во взгляде с классом ‘right’. Мы будем использовать тот же помощник по дате, и наш код будет выглядеть так:

1
2
# app/views/forums/show.html.erb
<td class=»right»><%= distance_of_time_in_words_to_now topic.last_post_at %> ago by <%= link_to topic.user.username, «/users/#{topic.last_poster_id}» %></td>

Теперь, если вы откроете свой браузер, вы должны увидеть:

Итак, теперь, когда у нас есть все в списках форумов и тем, мы собираемся работать над третьим основным представлением. Откройте файл app / views / themes / show.html.erb и отредактируйте его так, чтобы он выглядел примерно так:

01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
# app/views/topics/show.html.erb
<% title @topic.name %>
 
<% for post in @topic.posts %>
<div class=»post»>
  <span class=»left»><%= post.user.username %><br /><%= link_to «Delete», post, :confirm => ‘Are you sute?’, :method => :delete%>
  <span class=»right»><%= post.content %>
</div>
<% end %>
 
<p>
  <%= link_to «Edit», edit_topic_path(@topic) %> |
  <%= link_to «Destroy Topic», @topic, :confirm => ‘Are you sure?’, :method => :delete %> |
  <%= link_to «View All», topics_path %>
</p>

Давайте также добавим немного стиля:

1
2
3
4
5
# public/stylesheets/application.css
.post {width:100%;
.post span {padding:5px;
.post span.left {width:150px;
.post span.right { border:3px solid #eee;

И теперь просмотр темы будет выглядеть так:

Когда нажимается имя пользователя, мы хотим показать его профиль, правильно? Поэтому для этого откройте app / controllers / users_controller.rb и действие show ниже:

1
2
3
4
# app/controllers/users_controller.rb
def show
  @user = User.find(params[:id])
end

Это все, что нам понадобится для этого действия в нашем контроллере. Теперь создайте файл с именем show.html.erb в app / views / users и поместите в него следующее:

1
2
3
4
5
6
7
8
# app/views/users/show.html.erb
<% title ‘user: ‘ + @user.username %>
<div id=»topics»>
  <strong>topics</strong>
  <% @user.topics.each do |topic|
    <%= link_to topic.name, topic_url(topic.forum.id) %>
  <% end %>
</div>

Это простое количество кода будет отображать темы пользователя. Нет, здесь нет ограничений, поэтому мы технически покажем все темы этого пользователя. Наиболее идеальным решением было бы создание методов для обработки этого в нашей модели User, но я не буду здесь этого делать.

Итак, теперь, когда мы почти закончили, нам нужно поработать над функциями для редактирования сообщения. Давайте сначала откроем app / controllers / themes_controller.rb и наш взгляд на действие шоу. Сначала на контроллере мы собираемся удалить функциональность редактирования и обновления. Вы можете спросить, почему, и причина в том, что мы не будем позволять пользователю редактировать тему, а скорее постить. Да, это не позволяет пользователю изменять заголовок темы, но это лучше, чем написание дополнительной логики, необходимой для редактирования темы. Итак, удалите эти действия, и вы также можете удалить соответствующие представления.

Теперь откройте файл app / views / themes / show.html.erb. Сначала в теге абзаца внизу удалите ссылку «Редактировать», первую в списке. Затем перейдите к циклу, и перед ссылкой для удаления поста мы собираемся добавить ссылку для редактирования поста. Вид, когда мы закончим, должен выглядеть следующим образом:

01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
# app/views/topics/show.html.erb
<% title @topic.name %>
 
<% for post in @topic.posts %>
<div class=»post»>
  <span class=»left»><%= link_to post.user.username, user_url(post.user) %><br /><%= link_to «Edit», edit_post_path(post) %><br /><%= link_to «Delete», post, :confirm => ‘Are you sute?’, :method => :delete %>
  <span class=»right»><%= post.content %>
</div>
<% end %>
 
<p>
  <%= link_to «Reply», «#{new_post_path}?topic=#{@topic.id}» %> |
  <%= link_to «Destroy Topic», @topic, :confirm => ‘Are you sure?’, :method => :delete %> |
  <%= link_to «View All», forum_path(@topic.forum_id) %>
</p>

Мы также добавили ссылку для ответа по функциональности, которую добавим через минуту. Теперь, когда вы нажимаете на ссылку для редактирования, она должна работать. Затем, если вы попытаетесь изменить сообщение, Rails выдаст вам ошибку. Ну, мы не хотим, чтобы он перенаправлял туда, где он находится в данный момент, поэтому откройте app / controllers / posts_controller.rb, прокрутите вниз до действия create и найдите строку «redirect_to @post». Мы собираемся изменить строку, чтобы окончательное действие выглядело так:

01
02
03
04
05
06
07
08
09
10
# app/controllers/posts_controller.rb
def create
  @post = Post.new(:content => params[:post][:content], :topic_id => params[:post][:topic_id], :user_id => current_user.id)
  if @post.save
    flash[:notice] = «Successfully created post.»
    redirect_to «/topics/#{@post.topic_id}»
  else
    render :action => ‘new’
  end
end

Теперь этот код перенаправит нас на действие show для идентификатора форума сообщения. У нас есть только одна проблема: действие create не передается topic_id в представлении, поэтому откройте app / views / posts / _form.html.erb и добавьте следующую строку под сообщения об ошибках.

1
2
# app/views/posts/_form.html.erb
<% if params[:topic] %><input type=»hidden» id=»topic_forum_id» name=»post[topic_id]» value=»<%= params[:topic] %>» /><% end %>

Так что теперь, когда нам также нужно обновить тему, у нас есть новый пост. Таким образом, чтобы сделать это, мы собираемся снова изменить действие создания так

01
02
03
04
05
06
07
08
09
10
11
12
# app/controllers/posts_controller.rb
def create
  @post = Post.new(:content => params[:post][:content], :topic_id => params[:post][:topic_id], :user_id => current_user.id)
  if @post.save
    @topic = Topic.find(@post.topic_id)
    @topic.update_attributes(:last_poster_id => current_user.id, :last_post_at => Time.now)
    flash[:notice] = «Successfully created post.»
    redirect_to «/topics/#{@post.topic_id}»
  else
    render :action => ‘new’
  end
end

После того как сообщение было сохранено, мы использовали topic_id, чтобы найти нашу тему, а затем обновили два столбца в нашей базе данных. Идите дальше и скопируйте эти две строки, которые мы добавили в то же место в нашем действии обновления, чтобы это выглядело так:

01
02
03
04
05
06
07
08
09
10
11
12
# app/controllers/posts_controller.rb
def update
  @post = Post.find(params[:id])
  if @post.update_attributes(params[:post])
    @topic = Topic.find(@post.topic_id)
    @topic.update_attributes(:last_poster_id => current_user.id, :last_post_at => Time.now)
    flash[:notice] = «Successfully updated post.»
    redirect_to @post
  else
    render :action => ‘edit’
  end
end

Мы также изменили перенаправление на него, чтобы оно выполняло то же действие, что и действие create. Теперь перейдите к действию уничтожения и измените перенаправление на:

1
redirect_to forums_url

Теперь нам нужно быстро очистить наши представления для ссылок, которые не нужны или больше не работают. Прежде всего, действие show для форумов, поэтому откройте app / views / forums / show.html.erb и удалите ссылки show и edit так, чтобы вид выглядел следующим образом:

01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
16
17
18
19
20
# app/views/forums/show.html.erb
<% title @forum.name %>
 
<table>
  <tr>
    <th width=»60%»>Topic Title</th>
    <th width=»10%»>Replies</th>
    <th width=»30%»>Last Post</th>
  </tr>
  <% for topic in @forum.topics %>
    <tr>
      <td><%= link_to h(topic.name), topic_path(topic.id) %>
      <td><%= topic.posts.count — 1%></td>
      <td class=»right»><%= distance_of_time_in_words_to_now topic.last_post_at %> ago by <%= link_to topic.user.username, «/users/#{topic.last_poster_id}» %></td>
      <td><%= link_to «Destroy», topic, :confirm => ‘Are you sure?’, :method => :delete %></td>
    </tr>
  <% end %>
</table>
 
<p><%= link_to «New Topic», «/topics/new?forum=#{@forum.id}» %></p>

Нам также нужно обновить метод создания тем, чтобы он добавлял идентификатор пользователя нашего текущего пользователя, поэтому откройте app / controllers / themes_controller.rb и измените строку Topic.new на:

1
2
# app/controllers/topics_controller.rb
@topic = Topic.new(:name => params[:topic][:name], :last_poster_id => current_user.id, :last_post_at => Time.now, :forum_id => params[:topic][:forum_id], :user_id => current_user.id)

Нам также необходимо очистить файл app / views / forums / index.html.erb, поэтому откройте его и удалите ссылку show, чтобы файл выглядел следующим образом:

01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
16
17
18
19
20
21
# app/views/forums/index.html.erb
<% title «Forums» %>
 
<table>
  <tr>
    <th width=»70%»>Forum</th>
    <th width=»30%»>Last Post</th>
  </tr>
  <% for forum in @forums %>
    <tr>
      <td><h4><%= link_to h(forum.name), forum_path(forum.id) %></h4>
        <small><%= forum.topics.count %> topics</small><br />
        <%=h forum.description %></td>
      <td class=»right»><% if forum.most_recent_post %><%= distance_of_time_in_words_to_now forum.most_recent_post.last_post_at %> ago by <%= link_to forum.most_recent_post.user.username, «/users/#{forum.most_recent_post.last_poster_id}» %><% else %>no posts<% end %></td>
      <td><%= link_to «Edit», edit_forum_path(forum) %></td>
      <td><%= link_to «Destroy», forum, :confirm => ‘Are you sure?’, :method => :delete %></td>
    </tr>
  <% end %>
</table>
 
<p><%= link_to «New Forum», new_forum_path %></p>

В настоящее время любой пользователь может редактировать, удалять и создавать все, что он хочет. Итак, мы собираемся открыть наш помощник по приложениям в app / helpers / application_helper.rb. Мы добавим несколько дополнительных методов, которые помогут нам справиться с аутентификацией и уровнями пользователей. Перед тэгом ‘end’ добавьте эти методы:

01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
# app/helpers/application_helper.rb
def admin?
  if current_user.permission_level == 1 ||
    return true
  else
    return false
  end
end
 
def owner?(id)
  if current_user.id == id
    return true
  else
    return false
  end
end
 
def admin_or_owner?(id)
  if (admin? || owner?(id))
    return true
  else
    return false
  end
end

Эти методы позволяют нам выяснить, является ли пользователь администратором и владельцем, передавая user_id того, что мы ищем, и тот, который обрабатывает оба. Проверка метода для администратора также позволяет первому пользователю автоматически быть администратором. Итак, сначала мы собираемся открыть файл Forum index.html.erb. Мы собираемся добавить дополнительный код, чтобы скрыть создание, редактирование и удаление ссылок на форуме от кого-либо, кроме администраторов. Мы собираемся обернуть каждую из этих ссылок <% if admin? %> и <% end%>. Окончательный код будет выглядеть так:

01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
16
17
18
19
20
21
# app/views/forums/index.html.erb
<% title «Forums» %>
 
<table>
  <tr>
    <th width=»70%»>Forum</th>
    <th width=»30%»>Last Post</th>
  </tr>
  <% for forum in @forums %>
    <tr>
      <td><h4><%= link_to h(forum.name), forum_path(forum.id) %></h4>
        <small><%= forum.topics.count %> topics</small><br />
        <%=h forum.description %></td>
      <td class=»right»><% if forum.most_recent_post %><%= distance_of_time_in_words_to_now forum.most_recent_post.last_post_at %> ago by <%= link_to forum.most_recent_post.user.username, «/users/#{forum.most_recent_post.last_poster_id}» %><% else %>no posts<% end %></td>
      <% if admin?
      <% if admin?
    </tr>
  <% end %>
</table>
 
<p><% if admin?

Теперь, если вы попытаетесь перезагрузить страницу, вы получите сообщение об ошибке «undefined method `missions_level». Ошибка говорит о том, что нет столбца с именем access_level. Нам нужно создать миграцию, поэтому выполните следующие команды для создания и переноса изменений:

1
2
script/generate migration add_permissions_to_users permission_level:integer
rake db:migrate

Теперь все должно работать правильно. Теперь перейдем к действию show, поэтому откройте app / views / forums / show.html.erb и отредактируйте его так, чтобы он выглядел следующим образом:

01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
16
17
18
19
20
# app/views/forums/show.html.erb
<% title @forum.name %>
 
<table>
  <tr>
    <th width=»60%»>Topic Title</th>
    <th width=»10%»>Replies</th>
    <th width=»30%»>Last Post</th>
  </tr>
  <% for topic in @forum.topics %>
    <tr>
      <td><%= link_to h(topic.name), topic_path(topic.id) %>
      <td><%= topic.posts.count — 1%></td>
      <td class=»right»><%= distance_of_time_in_words_to_now topic.last_post_at %> ago by <%= link_to topic.user.username, «/users/#{topic.last_poster_id}» %></td>
      <% if admin?
    </tr>
  <% end %>
</table>
 
<p><% if logged_in?

То, что мы сделали, это сделали только администратора для действия по уничтожению, и вошли только действия по созданию темы. Теперь мы собираемся поработать над действием Показать темы, файл app / views / themes / show.html.erb. Откройте его и отредактируйте так, чтобы он выглядел следующим образом:

01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
# app/views/topics/show.html.erb
<% title @topic.name %>
 
<% for post in @topic.posts %>
<div class=»post»>
  <span class=»left»><%= link_to post.user.username, user_url(post.user) %><br /><% if logged_in?
  <span class=»right»><%= post.content %>
</div>
<% end %>
 
<p>
  <% if logged_in?
  <% if admin?
  <%= link_to «View All», forum_path(@topic.forum_id) %>
</p>

Мы снова просто внедрили некоторые методы, чтобы незарегистрированные пользователи и пользователи, у которых недостаточно прав, не могли видеть некоторые ссылки. Теперь, когда у нас заблокированы ссылки, мы собираемся заблокировать контроллеры, поэтому сначала откройте app / controllers / forum_controller.rb и в операторе справа под объявлением класса добавьте:

1
before_filter :admin_required, :except => [:index, :show]

Итак, теперь, если вы попробуете ваше приложение, вы заметите, что Rails выдаст нам ошибку, и это потому, что он не определил admin_required. Чтобы добавить это, мы собираемся определить его в нашем контроллере приложения, поэтому откройте его и добавьте следующий метод прямо перед последним конечным тегом:

1
2
3
4
5
6
# app/controllers/application_controller.rb
def admin_required
  unless current_user && (current_user.permission_level == 1 || current_user.id == 1)
    redirect_to ‘/’
  end
end

Это говорит о том, что если мы не являемся администратором, перенаправить на домашнюю страницу. Теперь мы продолжим работу с нашим контроллером тем, как и в контроллере форумов, добавим несколько фильтров перед фильтрами и на этот раз добавим:

1
2
3
# app/controllers/topics_controller.rb
before_filter :login_required, :except => [:index, :show]
before_filter :admin_required, :only => :destroy

Итак, мы говорим, что вы должны войти в систему для каждого действия, кроме index и show, и вы должны быть администратором, чтобы получить доступ к уничтожению. Далее мы собираемся поработать над нашим постом контроллера. Этот будет немного больше работы. Сначала добавьте фильтр before:

1
2
# app/controllers/posts_controller.rb
before_filter :login_required

Далее мы собираемся добавить дополнительный метод в наш файл application_controller.rb, чтобы справиться с этим. Вот метод, который мы собираемся добавить:

1
2
3
4
5
6
# app/controllers/application_controller.rb
def admin_or_owner_required(id)
  unless current_user.id == id ||
    redirect_to ‘/’
  end
end

Этот метод проверяет, равен ли переданный ему user_id идентификатору нашего текущего пользователя или наш текущий пользователь является администратором. Вернитесь к нашему контроллеру сообщений и отредактируйте действия редактирования, обновления и удаления, чтобы они выглядели так:

01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
# app/controllers/posts_controller.rb
def edit
  @post = Post.find(params[:id])
  admin_or_owner_required(@post.user.id)
end
 
def update
  @post = Post.find(params[:id])
  admin_or_owner_required(@post.user.id)
  if @post.update_attributes(params[:post])
    @topic = Topic.find(@post.topic_id)
    @topic.update_attributes(:last_poster_id => current_user.id, :last_post_at => Time.now)
    flash[:notice] = «Successfully updated post.»
    redirect_to «/topics/#{@post.topic_id}»
  else
    render :action => ‘edit’
  end
end
 
def destroy
  @post = Post.find(params[:id])
  admin_or_owner_required(@post.user.id)
  @post.destroy
  flash[:notice] = «Successfully destroyed post.»
  redirect_to forums_url
end

Теперь, когда мы завершили аутентификацию, у нас есть последний шаг.

Итак, теперь мы собираемся добавить заголовок в наш файл макета приложения. Откройте файл app / views / layouts / application.html.erb и добавьте следующее сразу после тега body:

01
02
03
04
05
06
07
08
09
10
11
# app/views/layouts/application.html.erb
<div id=»header»>
    <h1>My Forums!</h1>
    <% if logged_in?
        Welcome <%= current_user.username %>!
        <%= link_to «Log out», logout_path %>
    <% else %>
        <%= link_to «Sign up», signup_path %> or
        <%= link_to «log in», login_path %>.
    <% end %>
</div>

Теперь откройте наш CSS-файл и добавьте следующую строку:

1
2
# public/stylesheets/application.css
#header {width:75%;

Открыв веб-браузер, вы должны увидеть что-то вроде следующего:

Я надеюсь, вам понравился этот урок, и если вы пробились через это чудовище, я рекомендую вас! Если вы ищете форум, это не будет плохим началом, но Beast (в данном случае altered_beast) — отличный форум с действительно хорошим и плавным дизайном. Ниже приведен экран со статистикой о нашей программе, и для создания этого форума потребовалось 276 строк кода (исключая просмотры)!