Здравствуйте, записался на курс. При этом ставил галочку на "обучаться с тьютором". На email пришло письмо, о том, что записался на самостоятельное изучение курса. Как выбрать тьютора? |
Слежение за сообщениями пользователей
Веб-интерфейс для читаемых пользователей
Во введении к этой главе, мы сделали предварительный обзор страниц, необходимых для слежения за сообщениями пользователей. В этом разделе мы реализуем базовый интерфейс и читать/не читать функциональность, показанные в тех макетах. Мы также сделаем отдельные страницы для демонстрации массивов читателей и читаемых. В Разделе 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 содержит и количество читаемых пользователей, и количество его читателей, каждое из чисел должно быть ссылкой на соответствующую специальную страницу для их отображения. В Главе 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.
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
Мы подключим статистику к странице профиля через мгновение, но вначале давайте сделааем партиал для 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
Мы вскоре сделаем эти кнопки рабочими, фактически, мы сделаем это двумя способами, стандартным способом (Раздел 11.2.4) и с помощью Ajax (Раздел 11.2.5), но вначале мы закончим HTML интерфейс, создав страницы для списков читающих и читаемых.