Опубликован: 27.01.2016 | Доступ: свободный | Студентов: 914 / 58 | Длительность: 23:07:00
Лекция 11:

Слежение за сообщениями пользователей

< Лекция 10 || Лекция 11: 12345678

Веб-интерфейс для читаемых пользователей

Во введении к этой главе, мы сделали предварительный обзор страниц, необходимых для слежения за сообщениями пользователей. В этом разделе мы реализуем базовый интерфейс и читать/не читать функциональность, показанные в тех макетах. Мы также сделаем отдельные страницы для демонстрации массивов читателей и читаемых. В Разделе 11.3, мы завершим наш пример приложения, добавив поток сообщений пользователя.

Образцы данных

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

Когда мы последний раз видели заполнитель образцов данных в Листинге 11.20, он был немного суматошным. Поэтому мы начнем с определения отдельных методов для создания пользователей и микросообщений, а затем добавим образцы данных взаимоотношений используя новый метод make_relationships. Результаты показаны в Листинге 11.17.

namespace :db do
  desc "Fill database with sample data"
  task populate: :environment do
    make_users
    make_microposts
    make_relationships
  end
end

def make_users
  admin = User.create!(name:     "Example User",
                       email:    "example@railstutorial.org",
                       password: "foobar",
                       password_confirmation: "foobar",
                       admin: true)
  99.times do |n|
    name  = Faker::Name.name
    email = "example-#{n+1}@railstutorial.org"
    password  = "password"
    User.create!(name:     name,
                 email:    email,
                 password: password,
                 password_confirmation: password)
  end
end

def make_microposts
  users = User.all(limit: 6)
  50.times do
    content = Faker::Lorem.sentence(5)
    users.each { |user| user.microposts.create!(content: content) }
  end
end

def make_relationships
  users = User.all
  user  = users.first
  followed_users = users[2..50]
  followers      = users[3..40]
  followed_users.each { |followed| user.follow!(followed) }
  followers.each      { |follower| follower.follow!(user) }
end
Листинг 11.17. Добавление читатели/читаемые взаимоотношений в образцы данных. lib/tasks/sample_data.rake

Здесь образцы взаимоотношений создаются с помощью кода

def make_relationships
  users = User.all
  user  = users.first
  followed_users = users[2..50]
  followers      = users[3..40]
  followed_users.each { |followed| user.follow!(followed) }
  followers.each      { |follower| follower.follow!(user) }
end

Мы несколько произвольно организовали слежение первого пользователя за сообщениями пользователей с 3 по 51, а затем принудили пользователей с 4 по 41 читать сообщения первого пользователя. Полученных в результате взаимоотношений будет вполне достаточно для разработки интерфейса приложения.

Чтобы выполнить код Листинга 11.17, заполним базу данных как обычно:

$ bundle exec rake db:reset
$ bundle exec rake db:populate
$ bundle exec rake test:prepare

Статистика и форма для слежения за сообщениями пользователя

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

Как было отмечено в Разделе 11.1.1, слово "following" является двусмысленным при использовании в качестве атрибута (где user.following могло бы означать или читаемых пользователей или читателей пользователя), но оно вполне подходит в качестве метки, как в "50 following". Кроме того, это метка которой пользуется сам Twitter, использование принятое в набросках начиная с рис. 11.1 и показанное крупным планом на рис. 11.10.

Набросок партиала статистики.

Рис. 11.10. Набросок партиала статистики.

Статистика на рис. 11.10 содержит и количество читаемых пользователей, и количество его читателей, каждое из чисел должно быть ссылкой на соответствующую специальную страницу для их отображения. В Главе 5, мы временно заглушали подобные ссылки знаком ’#’, но это было до того, как мы набрались опыта с маршрутами. Сейчас, несмотря на то, что мы отложили сами страницы до Раздела 11.2.3, мы сделаем маршруты сейчас, как показано в Листинге 11.18. Этот код использует метод :member внутри блока resources, с которым мы ранее не были знакомы, но посмотрим, сможете ли вы угадать, что он делает. (Примечание: код в Листинге 11.18 должен заменить resources :users.)

SampleApp::Application.routes.draw do
  resources :users do
    member do
      get :following, :followers
    end
  end
  .
  .
  .
end
Листинг 11.18. Добавление действий following и followers в контроллер Users. config/routes.rb

Вы возможно догадываетесь, что URL для читаемых и читающих пользователей будут выглядеть как /users/1/following и /users/1/followers, и это именно то, что делает код в Листинге 11.18. Поскольку обе страницы будут отображать данные, мы используем get для того чтобы организовать ответ URL на запросы GET (что требуется конвенцией REST для подобных страниц), и метод member означает, что маршруты отвечают на URL, содержащие id пользователя. (Другой возможный метод, collection, работает без id, так что

resources :users do
  collection do
    get :tigers
  end
end

будет отвечать на URL /users/tigers (presumably to display all the tigers in our application). — предположительно для отображения всех тигров нашего приложения. Узнать больше об этой опции можно из Ruby on Rails по-русски "Роутинг в Rails". Таблица маршрутов, сгенерированных Листинге 11.18 представлена в Таблице 11.1; обратите внимание на именнованные маршруты для страниц с читателями и читаемыми, которые мы вскоре будем использовать. Неудачное гибридное применение в маршруте "following" обусловлено нашим решением использовать недвусмысленную терминологию "followed users" наряду с применением "following" взятым у Twitter. Поскольку предыдущий привел бы нас к маршрутам вида followed_users_user_path, что звучит довольно странно, мы выбрали последний в контексте Таблицы 11.1, что привело к following_user_path.

Таблица 11.1. RESTful маршруты предоставленные кастомными правилами в ресурсе из Листинга 11.18.
HTTP request URL Действие Именованный маршрут
GET /users/1/following following following_user_path(1)
GET /users/1/followers followers followers_user_path(1)

Определив маршруты мы готовы сделать тесты партиала статистики. (Мы могли бы начать с тестов, но именованные маршруты было бы трудно объяснить без обновленного файла маршрутов.) Партиал появится на странице профиля и на странице Home;

require 'spec_helper'

describe "Static pages" do
  .
  .
  .
  describe "Home page" do
    .
    .
    .
    describe "for signed-in users" do
      let(:user) { FactoryGirl.create(:user) }
      before do
        FactoryGirl.create(:micropost, user: user, content: "Lorem")
        FactoryGirl.create(:micropost, user: user, content: "Ipsum")
        sign_in user
        visit root_path
      end

      it "should render the user's feed" do
        user.feed.each do |item|
          expect(page).to have_selector("li##{item.id}", text: item.content)
        end
      end

      describe "follower/following counts" do
        let(:other_user) { FactoryGirl.create(:user) }
        before do
          other_user.follow!(user)
          visit root_path
        end

        it { should have_link("0 following", href: following_user_path(user)) }
        it { should have_link("1 followers", href: followers_user_path(user)) }
      end
    end
  end
  .
  .
  .
end
Листинг 11.19. Тестирование статистики читатели/читаемые на Home странице. spec/requests/static_pages_spec.rb

Ядром этих тестов является предположение, что количество читателей и читаемых представлено на странице совместно с правильными URL:

it { should have_link("0 following", href: following_user_path(user)) }
it { should have_link("1 followers", href: followers_user_path(user)) }

Здесь мы использовали именованные маршруты, показанные в Таблице 11.1 для проверки того, что ссылки имеют правильные адреса. Также обратите внимание на то, что в данном случае слово "followers" работает как метка, так что мы сохраним его во множественном числе, даже если будет только один читатель.

Код приложения для партиала статистики это просто несколько ссылок внутри div, как показано в Листинге 11.20.

<% @user ||= current_user %>
<div class="stats">
  <a href="<%= following_user_path(@user) %>">
    <strong id="following" class="stat">
      <%= @user.followed_users.count %>
    </strong>
    following
  </a>
  <a href="<%= followers_user_path(@user) %>">
    <strong id="followers" class="stat">
      <%= @user.followers.count %>
    </strong>
    followers
  </a>
</div>
Листинг 11.20. Партиал для отображения статистики. app/views/shared/_stats.html.erb

Поскольку мы включим статистику и на профиле пользователя и на Home странице, первая строка Листинга 11.20 выбирает правильное с помощью

<% @user ||= current_user %>

Как обсуждалось в Блоке 8.2, если @user не является nil, то ничего не происходит (как и на странице профиля), но когда он существует (как на странице Home), он назначает @user равным текущему пользователю.

Обратите внимание также на то, что количество читаемых/читателей подсчитывается через ассоциацию с помощью

@user.followed_users.count

и

@user.followers.count

Сравните это с подсчетом количества микросообщений из Листинга 11.17, где мы писали

@user.microposts.count

для подсчета микросообщений.

Одна последняя деталь которую стоит отметить - наличие CSS id у некоторых элементов, как в

<strong id="following" class="stat">
...
</strong>

Это сделано в угоду Ajax реализации из Раздела 11.2.5, которая получает доступ к элементам страницы используя их уникальные id.

С готовым партиалом включить статистику в Home страницу проще простого, как показано в Листинге 11.21. (Это также приводит к прохождению тестов из Листинга 11.19.)

<% if signed_in? %>
      .
      .
      .
      <section>
        <%= render 'shared/user_info' %>
      </section>
      <section>
        <%= render 'shared/stats' %>
      </section>
      <section>
        <%= render 'shared/micropost_form' %>
      </section>
      .
      .
      .
<% else %>
  .
  .
  .
<% end %>
Листинг 11.21. Добавление статистики к Home странице. app/views/static_pages/home.html.erb

Для того чтобы придать статистике стиль, мы добавим немного SCSS, как это показано в Листинге 11.22 (который содержит весь код таблиц стилей необходимый в этой главе). Результат представлен на рис. 11.11.

.
.
.

/* sidebar */
.
.
.
.stats {
  overflow: auto;
  a {
    float: left;
    padding: 0 10px;
    border-left: 1px solid $grayLighter;
    color: gray;
    &:first-child {
      padding-left: 0;
      border: 0;
    }
    &:hover {
      text-decoration: none;
      color: $blue;
    }
  }
  strong {
    display: block;
  }
}

.user_avatars {
  overflow: auto;
  margin-top: 10px;
  .gravatar {
    margin: 1px 1px;
  }
}
.
.
.
Листинг 11.22. SCSS для боковой панели страницы Home. app/assets/stylesheets/custom.css.scss
Home страница (/) со статистикой.

Рис. 11.11. Home страница (/) со статистикой.

Мы подключим статистику к странице профиля через мгновение, но вначале давайте сделааем партиал для follow/unfollow кнопки, как показано в Листинге 11.22.

<% unless current_user?(@user) %>
  <div id="follow_form">
  <% if current_user.following?(@user) %>
    <%= render 'unfollow' %>
  <% else %>
    <%= render 'follow' %>
  <% end %>
  </div>
<% end %>
Листинг 11.23. Партиал для формы follow/unfollow. app/views/users/_follow_form.html.erb

Этот партиал ничего не делает кроме перекладывания реальной работы на follow и unfollow партиалы, которым нужен новый файл маршрутов чьи правила для ресурса Relationships, следуют примеру ресурса Microposts (Листинг 11.22), как показано в Листинге 11.24.

SampleApp::Application.routes.draw do
  .
  .
  .
  resources :sessions,      only: [:new, :create, :destroy]
  resources :microposts,    only: [:create, :destroy]
  resources :relationships, only: [:create, :destroy]
  .
  .
  .
end
Листинг 11.24. Добавление маршрутов для пользовательских взаимоотношений. config/routes.rb

Сами партиалы follow/unfollow показаны в Листинге 11.25 и Листинге 11.26.

<%= form_for(current_user.relationships.build(followed_id: @user.id)) do |f| %>
  <div><%= f.hidden_field :followed_id %></div>
  <%= f.submit "Follow", class: "btn btn-large btn-primary" %>
<% end %>
Листинг 11.25. Форма для слежения за сообщениями пользователя. app/views/users/_follow.html.erb
<%= form_for(current_user.relationships.find_by(followed_id: @user),
             html: { method: :delete }) do |f| %>
  <%= f.submit "Unfollow", class: "btn btn-large" %>
<% end %>
Листинг 11.26. Форма для отписки от сообщений пользователя. app/views/users/_unfollow.html.erb

Обе эти формы используют form_for для манипуляций с объектом модели Relationship; основное отличие между ними заключается в том, что Листинг 11.25 строит новое взаимоотношение, тогда как Листинг 11.26 ищет существующее взаимоотношение. Естественно, первый отправляет POST запрос к контроллеру Relationships для create взаимоотношения, в то время как последний отправляет DELETE запрос для destroy взаимоотношения. (Мы напишем эти действия в Разделе 11.2.4.) Наконец, отметьте что форма follow/unfollow не содержит никакого контента кроме кнопки, но нам все еще необходимо отправить followed_id, чего мы можем добиться с помощью метода hidden_field из Листинга 11.25; который производит HTML вида

<input id="relationship_followed_id"
name="relationship[followed_id]"
type="hidden" value="3" />

Тег "hidden" input поместит соответствующую информацию на странице не отображая ее в браузере.

Теперь мы можем включить форму чтения и статистику на страницу профиля пользователя простым рендерингом партиалов, как показано в Листинге 11.27. Профили с follow и unfollow кнопками, соответственно, представлены на рис. 11.12 и рис. 11.13.

<% provide(:title, @user.name) %>
<div class="row">
  <aside class="span4">
    <section>
      <h1>
        <%= gravatar_for @user %>
        <%= @user.name %>
      </h1>
    </section>
    <section>
      <%= render 'shared/stats' %>
    </section>
  </aside>
  <div class="span8">
    <%= render 'follow_form' if signed_in? %>
    .
    .
    .
  </div>
</div>
Листинг 11.27. Добавление формы слежения за сообщениями пользователя и статистики на страницу профиля пользователя. app/views/users/show.html.erb
Профиль пользователя с follow кнопкой (/users/2).

Рис. 11.12. Профиль пользователя с follow кнопкой (/users/2).
Профиль пользователя с unfollow кнопкой (/users/6).

Рис. 11.13. Профиль пользователя с unfollow кнопкой (/users/6).

Мы вскоре сделаем эти кнопки рабочими, фактически, мы сделаем это двумя способами, стандартным способом (Раздел 11.2.4) и с помощью Ajax (Раздел 11.2.5), но вначале мы закончим HTML интерфейс, создав страницы для списков читающих и читаемых.

< Лекция 10 || Лекция 11: 12345678
Вадим Обозин
Вадим Обозин

Здравствуйте, записался на курс. При этом ставил галочку на "обучаться с тьютором". На email пришло письмо, о том, что записался на самостоятельное изучение курса. Как выбрать тьютора?