close

Вход

Забыли?

вход по аккаунту

Апатченков Данила Андреевич. Разработка и реализация веб-приложения обмена текстовыми сообщениями

код для вставки
АННОТАЦИЯ
ВКР 104 с., 30 рис., 3 табл., 18 источников, 1 прил.
ВЕБ-ПРИЛОЖЕНИЕ, ОБМЕН СООБЩЕНИЯМИ, RUBY ON RAILS,
МЕССЕНДЖЕР, MODEL-VIEW-CONTROLLER.
Выпускная квалификационная работа посвящена разработке и реализации
веб-приложения обмена текстовыми сообщениями.
В первой главе произведен анализ предметной области, описаны процессы
веб-приложения обмена текстовыми сообщениями, произведен анализ аналогов,
определены функциональные требования и нефункциональные требования к
разрабатываемому веб-приложению.
Во второй главе произведен выбор инструментальных средств разработки и
реализации веб-приложения обмена текстовыми сообщениями, спроектирована
БД, описана архитектура веб-приложения обмена текстовыми сообщениями.
В третьей главе описана реализация веб-приложения обмена текстовыми
сообщениями, как ее серверной части, так и клиентской, а также развертывание
веб-приложения в интернет сети.
В четвертой главе описывается работа законченного веб-приложения обмена
текстовыми сообщениями.
4
СОДЕРЖАНИЕ
ВВЕДЕНИЕ .................................................................................................................. 6
1 АНАЛИЗ ПРЕДМЕТНОЙ ОБЛАСТИ И ПОСТАНОВКА ЗАДАЧИ ..................... 8
1.1 Анализ предметной области .................................................................................. 8
1.2 Постановка задачи .................................................................................................. 9
1.3 Анализ аналогов ................................................................................................... 10
1.4 Функциональные требования .............................................................................. 11
1.5 Нефункциональные требования .......................................................................... 12
1.6 Диаграмма вариантов использования ................................................................. 12
1.7 Диаграмма классов ............................................................................................... 14
1.8 Диаграмма деятельности...................................................................................... 15
1.9 Диаграмма развертывания ................................................................................... 16
2
ПРОЕКТИРОВАНИЕ
ВЕБ-ПРИЛОЖЕНИЯ
ОБМЕНА
ТЕКСТОВЫМИ
СООБЩЕНИЯМИ...................................................................................................... 18
2.1 Обзор технологий для реализации ...................................................................... 18
2.2 Выбор технологии для использования ................................................................ 20
2.3 Клиент-серверная архитектура ............................................................................ 21
2.4 Общая архитектура веб-приложения .................................................................. 22
2.5 Взаимодействие клиента и сервера ..................................................................... 26
2.6 Сессия пользователя ............................................................................................ 28
2.7 Безопасность и шифрование ................................................................................ 29
2.8 Описание структуры данных ............................................................................... 30
2.9 Проектирование инфологической модели данных ............................................. 31
2.10 Проектирование пользовательского интерфейса ............................................. 33
5
2.11 Алгоритм обмена сообщениями ........................................................................ 38
3
РЕАЛИЗАЦИЯ
ВЕБ-ПРИЛОЖЕНИЯ
ОБМЕНА
ТЕКСТОВЫМИ
СООБЩЕНИЯМИ...................................................................................................... 41
3.1 Реализация серверной части ................................................................................ 41
3.2 Реализация клиентской части .............................................................................. 43
3.3 Развертывание веб-приложения обмена текстовыми собщениями в интернет
сети .............................................................................................................................. 44
4 ОПИСАНИЕ РАБОТЫ ВЕБ-ПРИЛОЖЕНИЯ ОБМЕНА ТЕКСТОВЫМИ
СООБЩЕНИЯМИ...................................................................................................... 45
ЗАКЛЮЧЕНИЕ .......................................................................................................... 55
СПИСОК ИСПОЛЬЗУЕМОЙ ЛИТЕРАТУРЫ ......................................................... 56
ПРИЛОЖЕНИЕ А (ОБЯЗАТЕЛЬНОЕ) .................................................................... 57
УДОСТОВЕРЯЮЩИЙ ЛИСТ ................................................................................ 102
ИНФОРМАЦИОННО-ПОИСКОВАЯ ХАРАКТЕРИСТИКА ДОКУМЕНТА НА
ЭЛЕКТРОННОМ НОСИТЕЛЕ ................................................................................ 103
6
ВВЕДЕНИЕ
В современном обществе информационные технологии прочно вошли в
жизнь каждого человека. В России и во всем мире наблюдается бурное развитие
средств и технологий, связанных с обменом информацией.
В данное время стали популярны информационные технологий, которые
стремительно развиваются каждую секунду нашей жизни. Технологический, а
также информационный прогресс, особенно за последние годы, сделал огромный
шаг вперед. Современное человечество быстро превращается в информационное
общество, а в особенности это происходит в быстроразвивающихся странах, которые делают большой упор именно на развитие техники и информационных технологий.
У многих людей есть компьютеры, будь то настольные персональные компьютеры, ноутбуки или карманные персональные копмьютеры, а также доступ во
всемирную сеть Интернет, с огромным количеством развлекательных и интересных ресурсов, либо в локальную сеть, которая наполнена различными развлекательными сервисами. Соответственно, люди знакомятся и становиться возможным общение на расстоянии, чтобы облегчить эту задачу, в наше время существует очень много решений, данный диплом был посвящен разработке средства общения между людьми в реальном времени на расстоянии.
Современные приложения должны быть безопасны, высокопроизводительны, работать в распределенной среде, быть нейтральны к архитектуре. Все эти
факторы привели к необходимости нового взгляда на сам процесс создания и распределения приложений на множестве машин различной архитектуры. Требования к переносимости заставили отказаться от традиционного способа создания и
доставки бинарных файлов, содержащих машинные коды и, следовательно, привязанных к определенной платформе.
Жизнь современного человека немыслима без средств связи: экспресспочта, телефонная и видео связь и самая многофункциональная связь — связь посредством интернета. Электронная почта, VoIP, видео и аудио-конференции, ча-
7
ты, форумы, службы мгновенных сообщений — все эти возможности делают общение современного человека разнообразным по содержанию и широте контактов. Электронная связь может обеспечить коммуникацию в режиме реального
времени, при одновременном участии собеседников в процессе обсуждения, или в
асинхронном режиме, с взаимодействием между пользователями по мере их появления в сети.
При рассмотрении общения в сети традиционно выделяют обмен текстовыми сообщениями, статическими изображениями, аудио и видеоданными. В некоторых программах используется комбинированный подход
Целью моей выпускной квалификационной работы является создание удобного и полезного в первую очередь с точки зрения пользователя веб приложения,
с помощью которого пользователи смогут обмениваться текстовыми сообщениями.
Дипломная работа состоит из четырех глав, введения, заключения, приложений и списка литературы. В первой главе рассматриваются существующие на
сегодняшний день аналогичные программы, выявляются их недостатки существующих приложений. Вторая глава содержит описание архитектуры, базовые
моменты программного продукта «Depeche», созданного в ходе выпускной квалификационной
работы.
В
третьей
главе
описывается
реализация
веб-
приложения, а также его развертывание в интернет сети. В четвертой главе
наглядно описывается работа веб приложения. Приложения содержат подробное
руководство пользователя и руководство программиста.
8
1 АНАЛИЗ ПРЕДМЕТНОЙ ОБЛАСТИ И ПОСТАНОВКА ЗАДАЧИ
1.1 Анализ предметной области
Основой современного общества является информационный обмен. Информация является одной из самых больших ценностей. С каждым днем обмен
существующей информацией растет. Растет и скорость обмена информацией.
Развивается множество видов представления данных, а также форма их передачи
и представления пользователям. Информация быстро устаревает. Для ее обновления используется различные системы связи с различной степенью оперативности. Именно задача обмена информацией стала причиной появления различного
рода сетей, технических информационных систем, в том числе компьютерных
сетей. Сначала сети объединяли отдельные компьютеры в пределах комнаты или
здания. Далее появились сети, объединявшие компьютеры разных городов и
стран. И сегодня мы можем наблюдать глобальную сеть, которая на самом деле
является объединением множества различных систем более низкого уровня. Благодаря сети Интернет человечество получило в свои руки уникальную систему,
все возможности которой до конца не исследованы до сих пор.
Сегодня одной из наиболее перспективных современных технологий интерактивного общения становиться Instant Messanging (англ. «мгновенное сообщение»). Instant Messanging это сетевой сервис обмена сообщениями в «реальном» времени, т.е. с фактически мгновенной доставкой, когда задержка не ощутима.
Объемные и перегруженные функционалом социальные сети становятся
громоздкими и неудобными, поэтому в наше время пользователи предпочитают
приложения специализирующиеся на одной конкретной задачей, но что бы это
приложение справлялось с ней на все сто. Поэтому, например, такие приложения
как: Telegram, Viber, WhatsApp имея одну конкретную задачу — обеспечить
пользователю возможность быстрого обмена сообщениями, или же, например
Instagram — лента, состоящая из фотографий, которые пользователи выкладывают самостоятельно, пользуются в наше время дичайшим спросом.
9
Российский филиал исследовательского концерна GfK (Gesellschaft fur
Konsumforschung) Group, 16.01.2018, опубликовал отчет "Проникновение Интернета в России: итоги 2017 года". Суммарный объем выборки Омнибуса GfK за
2017 год составил 12 000 респондента. GfK – надежный источник актуальной
информации о рынках и потребителях более чем в 100 странах мира. Аудитория
интернет-пользователей в возрасте от 16 лет и старше составила 87 миллионов
человек. На начало 2018 года доступом в интернет со смартфонов пользовались
больше половины россиян от 16 лет и старше (51,5%). Что в общих чертах говорит нам об огромной и растущей аудитории интернета.
В общей сложности уже 40% российских пользователей обмениваются информацией с помощью мессенджеров чаще или настолько же часто, как и по сотовой связи. Всего 11% пользователей не любят прибегать к мессенджерам и делают это только тогда, когда сотовой связи нет.
1.2 Постановка задачи
Основной задачей является создание удобного и полезного в первую очередь с точки зрения пользователя веб приложения, с помощью которого пользователи смогут обмениваться текстовыми сообщениями.
Для достижения вышеописанной цели передо мной поставлены следующие
задачи:
а) изучить технологии веб приложений;
б) изучить работу с Ruby on Rails;
в) разработать веб приложение, с помощью которого возможно:
– зарегистрировать новую учетную запись;
– обеспечить вход и выход из учетной записи;
– восстановить доступ к учетной записи через e-mail;
– изменить персональные данные учетной записи;
– обеспечить возможность отправлять и принимать личные сообщения от
пользователей;
10
1.3 Анализ аналогов
На сегодняшний день на рынке предоставлен огромный выбор мессенджеров с разным функционалом и возможностями, но каждый из них имеет как свои
достоинства, так и недостатки.
WhatsApp — популярная система обмена мгновенными сообщениями. Позволяет пересылать текст, видео, фото и другие данные через специальные чаты.
Из-за своего функционала считается отличной заменой сотовой связи, ибо используется всего лишь интернет-трафик.
Плюсы WhatsApp:
– достаточно известен в России и странах СНГ;
– программа синхронизирует список контактов с телефонной книгой вашего
гаджета т.к. регистрация происходит по телефонному номеру;
– программа c 2016-го года полностью бесплатна. В данный момент у приложения нет никаких встроенных покупок.
Минусы WhatsApp:
– нет официальной версии для iPad;
– программа не сильна в вопросах шифрования;
– программа перестала поддерживать iOS 6 и ниже;
Viber, по сути, выполняет тоже самое что и WhatsApp, однако не лишен
своих недостатков.
Плюсы Viber:
– огромное количество пользователей;
– помимо обычной переписки можно совершать платные звонки на городские и мобильные телефоны, даже если на них не установлено приложение Viber;
Минусы Viber:
– одновременное работать не нескольких устройствах не может, также, как
и WhatsApp;
– не сохраняет историю переписок;
– колоссальное количество спама;
11
Так же в разрабатываем веб приложений будет реализована отличительная
функция поддержки Markdown синтаксиса.
Markdown — язык разметки текстов. Такие тексты легко писать и читать.
Таким образом, с помощью небольших тегов можно будет изменить вид отправляемого сообщения.
В таблице 1 представлена сравнительная характеристика основных функций
приложения.
Таблица 1 – Сравнительная характеристика разрабатываемого веб-приложения с
аналогами
Функции / Приложения
WhatsApp
Viber
Веб-приложение
Шифрование пароля
Нет
Да
Да
Поддержка старых вер-
Нет
Нет
Да
Да
Нет
Да
Нет
Нет
Да
сий ОС
Сохранение истории переписок
Поддержка Markdown
синтаксиса
Таким образом следует учесть все требования к современным мессенджерам, а также сделать его достаточно удобным при использовании, чтобы при выборе он был предпочтительным для пользователей.
1.4 Функциональные требования
Веб-приложение должно включать в себя:
а) обработка вводимой пользователем информации;
б) сохранять, использовать, проверять и обновлять информацию в базе данных;
12
в) создавать и удалять сессию и cookie;
г) генерировать токен для восстановления доступа с помощью e-mail;
д) задействовать протокол WebSocket для обеспечения обмена сообщениями
в реальном времени;
е) проверка на правильность введенных данных при регистрации, входе, обновлении информации;
1.5 Нефункциональные требования
Для разрабатываемого модуля регистрации можно выделить следующие
нефункциональные требования:
– реализация интерфейса;
– обеспечение максимальной производительности загрузки данных;
– ориентированность на пользователей;
– поддержка целостности и безопасности данных, обусловленные нарушениями в работе web-сервера;
– простота дизайна;
1.6 Диаграмма вариантов использования
Диаграмма вариантов использования включает в себя конечное множество
вариантов использования, которые в итоге должны определять все возможные
свойства ожидаемого поведения приложения. Для удобства множество вариантов
использования может рассматриваться как отдельный пакет. Применение вариантов использования на всех стадиях работы над проектом позволяет определенно
достичь требуемого уровня унификации обозначений для представления функциональности подсистем и системы в целом, а также является мощным средством
последовательного уточнения требований к проектируемому приложению на основе их итеративного обсуждения со всеми заинтересованными специалистами.
На диаграмме вариантов использования, которая показана на рисунке 1,
изображены отношения между актерами и их вариантами использования.
13
Актером на данной диаграмме является пользователь приложения, который
изображен на рисунке в виде "человечка", варианты использования изображены в
виде эллипсов (среди них – регистрация, восстановление доступа, вход и выход в
учетную запись и так далее.). На диаграмме продемонстрированы все возможные
варианты использования приложения пользователем. Отношения между пользователем и вариантами использования показаны ассоциациями (стрелки на рисунке 1).
Рисунок 1 – Диаграмма вариантов использования
14
1.7 Диаграмма классов
Диаграмма классов — диаграмма, на которой продемонстрировано общее
число декларативных или статических элементов модели, таких как классы с атрибутами и операциями, а также связывающие их отношения.
На диаграмме классов, которая изображена на рисунке 2, показаны такие
классы как: процесс обработки – управляющий класс, который координирует все
остальные классы, имеет операцию – взаимодействие, а также атрибут – Алгоритм, т.е. способ обработки;
Интерфейс имеет операцию – взаимодействие с помощью вывода информации (предоставление информации пользователю системы).
База данных – класс-сущность, информация о которых должна храниться
постоянно и не уничтожаться с выключением системы. База данных непосредственно имеет атрибуты: пользователи, общая информация, сообщения, которые
являются таблицами базы данных, операцией является хранение данных.
Рисунок 2 – Диаграмма классов
15
1.8 Диаграмма деятельности
Для удобства и более подробной детализации было сделано две диаграммы
деятельности (для регистрации и для использования функций приложения соответственно).
Диаграмм деятельности наглядно демонстрирует особенности реализации
операций классов, когда необходимо представить алгоритмы их выполнения.
Диаграмма деятельности (рис. 3) выполнена с помощью дорожек для наглядного
представления в каком состояние находится система на разных этапах и какое
подразделение за него отвечает.
Рисунок 3 – Диаграмма деятельности (регистрация)
Пользователю при регистрации необходимо ввести свои данные, программа
проверяет их корректность (проверка обязательных полей, запрет специальных
символов) и доступность (доступность логина, регистрации на вводимую почту),
если данные введены корректно , то данные записываются в базу данных и пользователь получает сообщение об успешной регистрации, с последующей возможностью войти под своей учетной записью , в противном случае , его возвращает на
форму регистрации с сообщением об ошибке в введенных данных.
16
На рисунке 4 непосредственно показаны все возможные варианты взаимодействия пользователя с приложением, благодаря диаграмме деятельности
наглядно продемонстрированы состояния, в которых находится приложение.
Рисунок 4 – Диаграмма деятельности (работа с приложением)
1.9 Диаграмма развертывания
Диаграмма развертывания, которая показана на рисунке 5, демонстрирует
на какой платформе и каких вычислительных средствах реализовано вебприложение, что дает полное физическое представление о проектируемом приложении.
На данной диаграмме показано, что устройства пользователей посредством
сети интернет входят в защищенную сеть и взаимодействует с сервером. Сервер
аналогично взаимодействует с устройствами пользователей.
17
Рисунок 5 – Диаграмма развертывания
18
2 ПРОЕКТИРОВАНИЕ ВЕБ-ПРИЛОЖЕНИЯ ОБМЕНА ТЕКСТОВЫМИ СООБЩЕНИЯМИ
2.1 Обзор технологий для реализации
Для решения нашей задачи есть великое множество способов.
JavaScript и Express фреймворк.
JavaScript - мультипарадигменный язык программирования, который поддерживает объектно-ориентированный, императивный и функциональные стили.
JavaScript - реализация языка ECMAScript
JavaScript можно использовать как встраиваемый язык для программного
доступа к объектам приложений.
Наиболее широко применяется в браузерах как язык сценариев для придания интерактивности веб-страницам.
Основные архитектурные черты: слабая типизация, динамическая типизация, автоматическое управление памятью, прототипное программирование, функции как объекты первого класса.
На JavaScript оказали влияние многие языки, при разработке была цель сделать язык похожим на Java, но при этом легким для использования для тех, кто не
умеет программировать.
Языком JavaScript не владеет определенная компания или организация, что
отличает его от ряда языков программирования, используемых в веб разработке.
Express в свою очередь это минималистичный и гибкий веб-фреймворк для
приложений Node.js, предоставляющий обширный набор функций для мобильных
и веб-приложений.
PHP.
PHP (англ. PHP: Hypertext Preprocessor — «PHP: препроцессор гипертекста»; первоначально Personal Home Page Tools — «Инструменты для создания
персональных веб-страниц») — скриптовый язык общего назначения, применяющегося для разработки веб-приложений. В настоящее время поддерживается по-
19
давляющим большинством хостинг-провайдеров и является одним из лидеров
среди языков, применяющихся для создания динамических веб-сайтов.
Язык и его интерпретатор (Zend Engine) разрабатываются группой энтузиастов в рамках проекта с открытым кодом. Проект распространяется под собственной лицензией, несовместимой с GNU GPL.
Python и Django фреймворки.
Django — бесплатный и свободный фреймворк для веб-приложений, написанный на Python. Фреймворк — это набор компонентов, которые помогают разрабатывать веб-сайты быстро и просто.
Каждый раз при разработке веб-сайтов требуются похожие компоненты:
способ аутентифицировать пользователей (вход, выход, регистрация), панель
управления сайтом, формы, инструменты для загрузки файлов и т. д.
К счастью для нас, другие люди обратили внимание на возникновение однотипных проблем при веб-разработке, так что они объединились и создали
фреймворки (Django и другие), которые предлагают нам готовые шаблоны для
использования.
Фреймворки существуют, чтобы облегчить процесс разработки и позволить
нам не изобретать колесо.
Хочется отметить, что основные принципы как в Ruby, PHP совпадают.
ASP .NET
Active Storage Pages для .NET — платформа разработки веб-приложений, в
состав которой входят: веб-сервисы, программная инфаструктура, модель программирования от компании Microsoft.
Ruby и фремворк Ruby on Rails.
Ruby в свою очередь является динамичным, с открытым кодом языком программирования сфокусированном на простоте и продуктивности, имея при себе
элегантный синтаксис, который легок для чтения и написания.
Особенностью Ruby on Rails является архитектура Model-View-Controller.
Также при себе имеет все стандартные и необходимые реализации, как и другие
веб-фрейморки, ориентированные на создание веб-приложений. Ruby on Rails
20
имеет огромное и отзывчивое комьюнити. Является отличным фреймворком для
стартапов.
Ruby On Rails существенно использовался при создании таких популярных
сайтов, как Twitter, SoundCloud, Basecamp, Github, Hulu, Kickstarter, Change.org.
По сравнению с остальными языками Ruby показался наиболее удобным.
Так как в языке Ruby присутствует большой выбор библиотек или как их называет сообщество - гемы. Если даже сравнить, как выглядит реализация на других
языках, на языке Ruby более понятен синтаксис. Для реализации веб приложения
были найдены все соответствующие модули и библиотеки, которые понадобятся в
создании данного продукта.
2.2 Выбор технологий для использования
Для нашего проекта удобный интерфейс пользователя можно представить с
помощью веб-сайта.
Для того, чтобы реализовать данное решение, необходимо всего лишь иметь
любой браузер под рукой. Также для реализации нам нужно будет написать интерфейс с нуля. Создание кнопок, переходов, состояний достаточно затратная обработка информации и так далее.
Веб-сайт, в котором будет задействовано приложение, является универсальным и кроссплатформенным поэтому на него можно будет зайти с мобильных
операционных систем таких как: iOS, Android, Windows Phone, так и с полноценных настольных решений таких как: MacOS, Windows, Linux. Тем самым мы можем подключиться к сайту с помощью разных платформ.
Способов реализации веб приложения для обмена текстовыми сообщениями
на различных языках программирования достаточно.
Изначально мы рассмотрели несколько вариантов реализации на разных
языках программирования и определили наиболее подходящий для нас это язык
Ruby и фреймворк Ruby on Rails.
21
2.3 Клиент-серверная архитектура
Приложение является многопользовательским и предназначено для использования в сети. Традиционно для такого рода приложений применяются два подхода: централизованное управление передачей сообщений между пользователями
или распределение функций синхронизации сообщений между всеми участниками сетевого взаимодействия.
Первый подход является, по сути, применением технологии «клиентсервер». При этом подходе создается выделенный сервер, который обслуживает
запросы клиентов, хранит данные для управления состоянием клиентов и выполняет синхронизацию данных между ними. Он отвечает за прием данных от каждого клиента и распространение этих данных между другими. В этом случае все
клиенты являются равноправными относительно распределения нагрузки и взаимодействуют исключительно с сервером.
Иногда серверная часть может включаться в клиентское приложение. В
этом случае происходит взаимодействие типа «клиент-клиент». Тогда один из
клиентов по предварительной договоренности несет на себе функции сервера, в
результате являясь и клиентом, и сервером. Такая реализация в случае большого
количества пользователей приводит к излишней нагрузке на одного из участников. Это, несомненно, является узким местом такого подхода. Еще одним из его
недостатков является «утяжеление» клиентской части. В дополнение к сказанному заметим, что совмещение функций клиента и сервера в одном приложении
может быть оправдано при реализации подключения «точка-точка», когда обе
стороны в разное время могут меняться ролями.
Второй подход основан на сетевом взаимодействии без использования сервера, как такового. В этом случае каждый участник коммуникационного процесса
сам осуществляет рассылку данных всем остальным участникам, например, посредством широковещания. Это позволяет сэкономить требуемые на обеспечение
поддержки сервера ресурсы. Но такая реализация, в основном, подходит для коммуникации в условиях локальной сети.
22
Наше приложение должно позволять пользователям общаться посредством
интернета. В этих условиях для обеспечения коммуникации множества пользователей применение клиент-серверной технологии обеспечит следующие преимущества:
– минимизация нагрузки на клиент;
– отсутствие необходимости устанавливать соединение напрямую между
клиентами;
– централизация по обслуживанию клиентов в одном месте.
2.4 Общая архитектура веб-приложения обмена текстовыми сообщениями
Для полного описания общей структуры приложения прибегнем к описанию
фреймворка Ruby On Rails, так как этот фреймворк предоставляет гибкие средства
для разработки распределенных приложений.
Ruby on Rails (RoR) — написанный на языке программирования, непосредственно реализует архитектурный шаблон Model-View-Controller для, а также
обеспечивает их интеграцию с веб-сервером и сервером баз данных. Является открытым программным обеспечением и распространяется под лицензией MIT.
В свою очередь MVC (Model-View-Controller) — это паттерн архитектуры
приложения, четко разделяющий на конкретные обязанности три его компонента:
– Model (Модель) является сутью приложения и отвечает за непосредственные алгоритмы, расчеты и тому подобное внутреннее устройство приложения.
Также предоставляет ссылку к хранилищу данных;
– View (Вид) необходим для вывода данных, предоставленных Моделью.
Это единственная часть MVC, которая непосредственно взаимодействует с пользователем;
– Controller (Контроллер) получает данные от пользователей и отправляет
их в Модель. Помимо этого, он получает сообщения от Модели и передает их в
Вид.
Общая схема взаимодействия в архитектуре MVC представлена на рисунке
6.
23
Рисунок 6 – Общая схема MVC
Однако нам нужно разобрать ее более детально.
Предположим, что есть страница, на которую переходит пользователь с целью получения списка всех пользователей, зарегистрировавшихся на этом сайте.
MVC в действии представлен на рисунке 7.
Рисунок 7 – Подробная схема MVC
Вот краткое изложение действий, показанных на рисунке 7:
1. Браузер выдает запрос на /users URL (пользователь переходит по адресу
сайте /users)
2. Маршрутиризатор Rails реагирует на action (действие) index в Users Контроллер
24
3. index действие запрашивает User Модель, чтобы получить всех пользователей;
4. User Модель тянет всех пользователей из базы данных;
5. User Модель возвращает список всех пользователей Контроллеру;
6. Контроллер захватывает всех пользователей в переменную @users, которая передается в Представление index;
7. Представление отображает страницу как HTML;
8. Контроллер передает HTML страницу с результатом в браузер.
Исходя из этого Ruby On Rails взаимодействует с тремя компонентами:
1. Active Record — это Модель в RoR. Модель хранит данные и предоставляет базу для работы с ними. Помимо этого, Active Record также является ORM
фреймворком. ORM значит Object-relational mapping (Объектно-реляционная проекция). Собственно, Active Record делает следующие вещи:
– соединение с базой данных. Вы можете подключиться к БД, используя
API, предоставляемый Active Record, который в свою очередь создает необходимый вам запрос непосредственно в движок БД при помощи адаптеров. У Active
Record есть адаптеры для MySQL, Postgres, MS SQLServer, DB2, SQLite. Необходимо лишь обозначить параметры доступа к БД в файле database.yml;
– проекция таблицы на класс. Каждая таблица проецируется непосредственно на один или несколько классов по принципу convention over configuration (соглашение выше конфигурации). Одно из таких соглашений – имя таблицы должно
быть во множественном числе, а название класса – в единственном. Атрибуты
таблицы сразу проецируются в атрибуты экземпляра Ruby. После того, как все
проекции сделаны, каждый объект ORM класса представляет определенную строку таблицы, с которой класс был спроецирован;
– операции CRUD. Это операции create (создание), retrieve (получение),
update (обновление) и delete (удаление) над таблицей. Поскольку Active Record –
это ORM фреймворк, вы всегда работаете с объектами. Чтобы создать новую
строку таблицы, вы создаете новый объект класса и заполняете его переменные
экземпляра значениями. Стоит отметить, что все это Active Record делает за вас;
25
– проверка данных. Проверка данных перед помещением их в таблицу – это
первый шаг в безопасности вашего проекта. Active Record предоставляет проверку Модели. Данные могут быть проверены автоматически с помощью множества
готовых методов, которые, в случае надобности, можно переписать под собственные нужды;
2. Action View. Вид включает в себя логику, которая необходима для вывода
данных Модели. Роль Вида в RoR играет Action View. Наиболее часто используемые функции Action View:
– шаблоны (Templates). Шаблоны – это файлы, которые содержат заполнители (placeholders), которые в свою очередь буду заменены на контент. Шаблоны
должны содержать HTML-код и код Ruby, встраиваемый в HTML с использованием синтаксиса встроенного (embedded) Ruby (ERb);
– помощники (helper, далее хелпер) форм и форматирования. Хелперы форм
позволяют создавать такие элементы страниц, как чекбоксы, списки, используя
реализованные методы. В свою очередь хелперы форматирования позволяют
форматировать данные нужным нам способом, методы существуют для дат, валют и строк;
– макет. Макеты (layouts) определяют, как контент будет расположен на
странице. Динамически создаваемая страница может содержать вложение из нескольких страниц, даже без использования таблиц и фрэймов, благодаря использованию API Макета;
3. Action Controller. В веб-приложении Контроллер регулирует поток логики
приложения. Он находится на краю программы, перехватывая все запросы, на основании которых он изменяет какой-либо объект Модели и вызывает Вид, чтобы
показанать обновленные данные. В RoR Action Controller является Контроллером,
вот его основные функции:
– поддержка сессий. Сессия – это период времени, проведенный пользователем на сайте или веб-приложении. Его можно отследить с помощью cookie или
объекта сессии. Cookie – небольшой файл, он не может содержать объекты, в отличие от объекта сессии;
26
– фильтрация. Бывают случаи, когда нужно вызвать определенный код, перед тем как исполнять логику Контроллера, или после него, например, аутентификация пользователей, логирование событий, предоставление персонального ответа. Помогают в таких случаях фильтры, которые предоставляет Action
Controller. Существуют всего три основных фильтра: before, after и around;
– кэширование. Кэширование – это процесс, при котором наиболее запрашиваемый контент сохраняется в кэше, чтобы не появлялась необходимость запрашивать его снова и снова;
Из всего этого вытекают достаточно очевидные преимущества: бизнеслогика отделяется от пользовательского интерфейса; максимальное использование механизмов повторного использования, которые позволяют минимизировать
дублирование кода в приложениях (принцип Don`t repeat yourself); приложение
легко обслуживать и поддерживать, потому что по умолчанию используются соглашения о конфигурации, типичные для большинства приложений (принцип
Convention over configuration).
2.5 Взаимодействие клиента и сервера
Взаимодействие между клиентов и сервером основано на протоколе полнодуплексной связи поверх TCP-соединения, предназначенном для обмена сообщениями между браузером и веб-сервером в режиме реального времени. Этот протокол называется WebSocket.
Веб-сокеты, в отличие от HTTP, позволяют работать с двунаправленным
потоком данных, что делает эту технологию совершенно уникальной.
При HTTP (или HTTPS) запросе браузер постоянно спрашивает у сервера,
есть ли для него новые сообщения, и получает их.
HTTP позволяет использовать разные типы запросов, такие как POST, GET
или PUT, каждый из которых имеет своё назначение.
На рисунке 8 показана схема обмена сообщениями по HTTP
27
Рисунок 8 – Схема обмена сообщениями по HTTP
Веб-сокетам же для ответа не нужны ваши повторяющиеся запросы. Достаточно выполнить один запрос и ждать отклика. Вы можете просто слушать сервер, который будет отправлять вам сообщения по мере готовности. Соответствующая схема представлена на рисунке 9.
Поэтому веб-сокеты идеально подойдут для нашего веб приложения.
28
Рисунок 9 – Схема обмена сообщениями при использовании веб-сокетов
2.6 Сессия пользователя
Сессия – это цикл работы клиента с сервером, в течение которого клиент
может производить запросы, результат которых логически взаимосвязан с предыдущими запросами. Для обеспечения взаимосвязанности таких запросов применяется управление состоянием, которое может реализовываться как на клиентской,
так и на серверной стороне.
Для того чтобы сервер мог идентифицировать конкретного клиента или
приложение и узнать о его состоянии используются cookies. Когда клиент делает
29
первоначальный запрос к серверу, сервер инициализирует сессию и передает клиенту небольшой кусок данных – cookie. Эти данные впоследствии хранятся на
стороне клиента в виде файла или в оперативной памяти. Клиент, осуществляющий запрос, добавляет cookie к запросу, а сервер может прочитать cookie, выделить необходимую информацию и соотнести запрос с соответствующими данными о состоянии пользователя.
Другим вариантом предоставления серверу информации о состоянии клиента является хранение параметров состояния на стороне клиента и передача этих
параметров серверу с каждым запросом. Но при этом, если данные состояния
имеют сложную структуру и большой объем, существенно возрастает объем передаваемой серверу информации.
2.7 Безопасность и шифрование
Веб приложение использует протокол SSL.
Протокол SSL (от англ. Secure Sockets Layer – уровень защищенных сокетов) используется миллионами сайтов для защиты данных в Интернете.
Он гарантирует безопасное соединение между браузером пользователя и сервером.
При
использовании
SSL-протокола
информация
передается
в закодированном виде по HTTPS и расшифровать ее можно только с помощью
специального ключа в отличие от привычного протокола HTTP. Для работы SSLпротокола требуется, чтобы на сервере был установлен SSL-сертификат.
SSL-сертификат – это своего рода уникальная цифровая подпись вашего
сайта. Такой сертификат нужен, в первую очередь, банкам, платежным системам
и другим организациям, работающим с персональными данными, – для защиты
транзакций и предотвращения несанкционированного доступа к информации.
На рисунке 10 представлен SSL-сертификат веб-приложения.
30
Рисунок 10 – SSL-сертификат
Для шифрования паролей пользователей в базе данных используется библиотека bcrypt.
Bcrypt — адаптивная криптографическая функция формирования ключа,
используемая для защищенного хранения паролей. Разработчики: Нильс Провос и
David Mazières. Функция основана на шифре Blowfish, впервые представлена
на USENIX в 1999 году. Для защиты от атак с помощью радужных таблиц bcrypt
использует соль (salt); кроме того, функция является адаптивной, время её работы
легко настраивается и её можно замедлить, чтобы усложнить атаку перебором.
2.8 Описание структуры данных
В базе данных веб приложения должна хранится следующая информация:
1. Информация о пользователе:
– логин;
– e-mail;
31
– пароль в хешированном виде;
– хеш-значение запоминания;
– хеш-значение восстановления;
– дата восстановления;
– дата регистрации.
2. Информация о диалоге:
– id отправителя;
– id получателя;
–дата создания.
3. Информация о сообщении:
– текст сообщения;
– id пользователя (отправителя);
– id диалога;
– дата создания;
– прикрепленные данные.
2.9 Проектирование инфологической модели
Для наглядного отображения объектов проектируемого приложения была
составлена инфологическая модель, представленная на рисунке 11. Она показывается все существующие классы объектов (сущности) и связи между ними.
Рисунок 11 – Инфологическая модель
В веб-приложении можно выделить следующие основные сущности: «Пользователь», «Диалог», «Сообщение».
32
Каждая сущность имеет определенный набор атрибутов (свойств).
У каждого класса объектов есть атрибут «идентификатор» - уникальный
номер, позволяющий однозначно идентифицировать (определить) класс в базе
данных.
Таблица «Пользователи» хранит основную информацию о зарегистрированных в системе пользователях. Назначение полей:
– ID пользователя – уникальный идентификатор пользователя в системе;
– имя – имя пользователя;
– e-mail – почта, является уникальной для каждого пользователя и используется для авторизации в приложении;
– дата регистрации – дата и время создания аккаунта пользователя;
– хеш-значение пароля – зашифрованный пароль пользователя;
– хеш-значение запоминания – хэш для использования cookie, чтобы пользователь оставался авторизованным даже после закрытия и повторного открытия
браузера;
– хеш-значение восстановления – токен для восстановления пароля
– дата восстановления – время действия токена
– администратор — булево значение, определяющее является ли пользователь администратором
Таблица «Диалоги» хранит основную информацию о диалогах пользователей. Назначение полей:
– ID диалога – уникальный идентификатор диалога в системе;
– ID пользователя-отправителя – код отправителя сообщений;
– ID пользователя-получателя – код получателя сообщений;
– дата создания – дата и время создания диалога;
Таблица «Сообщения» хранит основную информацию о сообщениях пользователей. Назначение полей:
– ID сообщения – идентификатор сообщения в системе;
– ID пользователя-отправителя – код отправителя сообщения;
– ID диалога – код диалога, к которому принадлежит сообщение;
33
– текст – тело сообщения;
– прикрепленные данные – документы, которыми обмениваются пользователи;
– дата создания – дата и время создания сообщение;
Сущность «Пользователь» имеет отношение «один-ко-многим» к сущности
«Сообщение», а также к сущности «Диалог», поскольку один пользователь может
иметь несколько как сообщений, так и диалогов с другими пользователями. В
свою очередь сущность «Диалог» также имеет отношение «один-ко-многим» к
сущности «Сообщение», поскольку в одном диалоге может содержаться огромное
количество сообщений, но не наоборот.
Отдельная таблица сущности «Диалог» нужна для того, чтобы мы могли
объединить отправителя сообщения и получателя (и наоборот) в одну «диалоговую комнату», которая будет видна только ее участникам, остальные же пользователи не смогут иметь доступ к такого рода переписке.
2.10 Построение пользовательского интерфейса
В процессе разработки диалога с пользователем, как правило, выделяют ряд
состояний системы, в которых она останется неизменной, и пользователь может
выполнять некоторые действия.
Логику диалога пользователя с приложением целесообразно описать использованием транзитивной сети (рис. 12).
Рисунок 12 – Транзитивная сеть
34
35
В таблице 2 приведено описание состояний транзитивной сети.
Таблица 2 – Описание состояний транзитивной сети
Состояние
Описание
S0
Стартовое состояние, пользователь запускает веб
приложение
Sreg
Регистрация
Slogin
Входим в учетную запись
Sprofile
Аутентификация, вход на страницу пользователя
Ssettings
Заходим в настройки аккаунта
Slist
Заходим на страницу списка пользователей
Schat
Заходим в чат
Slogout
Выходим из учетной записи
F0
Завершаем работу
В таблице 3 приведено описание переходов транзитивной сети
Таблица 3 – Описание переходов транзитивной сети
№ Дуги
Входной сигнал
1
2
Реакция на входной
сигнал
3
1
Пользователь открывает веб
приложение в браузере
Страница
приложения
2
Нажатие кнопка регистрации
Переход на страницу
регистрации
3
Нажатие кнопка входа
Переход на страницу
входа
4,5
Аутентификация после
регистрации/входа
Переход на страницу
профиля,
пользователь вошел в
личный аккаунт
Нажатие кнопка настроек
Переход на страницу
настроек
6, 39
36
Продолжение таблица 3
1
2
3
Нажатие кнопки чата
Переход на страницу
чата
8-10, 1416, 34, 38
Закрытие браузера
Завершение работы
11-13, 37
Нажатие кнопки
учетной записи
7,41
выхода
из
Выход из аккаунта
17
Выход из учетной записи
Переадресация
на
домашнюю страницу
18
Нажатие кнопки регистрации с
незаполненными полями
Вывод
соответствующей
ошибки
19
Введен слишком короткий логин
Вывод
соответствующей
ошибки
20
Введен занятый логин
Вывод
соответствующей
ошибки
21
Введен некорректный e-mail
Вывод
соответствующей
ошибки
22
Введен занятый e-mail
Вывод
соответствующей
ошибки
23
Введен
пароль
24
Подтверждение
совпадает
25
Все поля заполнены корректно
Переадресация
на
страницу профиля и
аутентификация
26
Нажатие
страницы
Переход
на
соответствующую
страницу
слишком
кнопки
короткий
пароля
не
домашней
Вывод
соответствующей
ошибки
Вывод
соответствующей
ошибки
37
Продолжение таблицы 3
1
2
3
27
Ввод некорректного e-mail
Вывод
соответствующей
ошибки
28
Ввод неправильного пароля
Вывод
соответствующей
ошибки
29
Все поля заполнены корректно
Переход на страницу
профиля
и
аутентификация
30
Нажатие кнопки регистрации
Переход
на
соответствующую
страницу
31
Нажатие кнопки восстановления
пароля
Переход
на
соответствующую
страницу
32
Некорректно введенный e-mail
Вывод
соответствующей
ошибки
33
Все поля заполнены корректно
Вывод сообщения о
восстановлении
пароля
и
переадресация
на
домашнюю страницу
Нажатие кнопки профиль
Переход
на
соответствующую
страницу
34-35
списка
Переход
на
соответствующую
страницу
43
Нажатие на пользователя, кому
хотим отправить сообщение
Открытие диалога с
соответствующим
пользователем
44
Вводим сообщение
Отправка сообщения
36, 40, 42
Нажатие
кнопки
пользователей
38
2.11 Алгоритм обмена сообщениями
Обмен текстовыми сообщениями в реальном времени происходит с помощью протокола веб-сокетов, реализация и api которого в Ruby on Rails называется
Action Cable. Таким образом, чтобы разработать алгоритм необходимо выяснить
как работает Action Cable, как минимум на высоком уровне.
Рассмотрим соответствующую схему на рисунке 13.
Рисунок 13 – Схема работы Action Cable
Рассмотрим работу Action Cable:
1. Существует отношение pub/sub (публикации/подписки) потоков, приводящих в действие функцию для Redis. Каждый поток использует отдельный канал
для Redis, что является хорошей практикой.
2. Сообщение распределяется через pub/sub поток. Каждое сообщение публикуется через широковещательный поток в Action Cable.
3. Клиентские подключение к серверу производятся с использованием протокола веб-сокетов.
39
4. Контроль того, какие данные будут распространяться клиентам осуществляется через классы канала (Channel), который может вызывать какие-либо
бизнес-логики, которые могут существовать в приложении.
Таким образом, прежде чем пользователи смогут обмениваться сообщениями в реальном времени, необходимо установить соединение между ними (рис.14).
Рисунок 14 – Подключение пользователей
Каждый клиент, пройдя регистрацию или войдя в существующий аккаунт
автоматически отправляет запрос на инициализацию соединения с помощью вебсокетов. А сервер взаимодействует с каждым клиентом с помощью постоянной
полнодуплексной связи.
После этого осуществляется основной алгоритм обмена текстовыми сообщениями, который показан на рисунке 15.
40
Рисунок 15 – Алгоритм обмена текстовыми сообщениями
Рассмотрим алгоритм обмена текстовыми сообщениями:
1. Пользователь выбирает собеседника переходя в соответствующий диалог
и вводит сообщение в соответствующую форму и нажимает кнопку отправить.
2. С помощью контроллера сообщений и специального метода для создания
сообщений, текст сообщения передается в базу данных Redis в формате JSON, после которого передается в канал диалога.
3. Канал диалога использует специальный метод subscribed, который предоставляет механизм потоков, с помощью которого каналы направляют опубликованный контент (трансляции) их подписчикам.
4. Пользователь подписывается на канал и действует как подписчик, затем
входящие сообщения направляются на эти подписки на канал, основываясь на
идентификаторе, посланным потребителем cable.
5. Сообщения отображаются в дереве DOM используя jquery.
41
3 РЕАЛИЗАЦИЯ ВЕБ-ПРИЛОЖЕНИЯ ОБМЕНА ТЕКСТО ВЫМИ
СООБЩЕНИЯМИ
3.1 Реализация серверной части
Серверная часть получает запрос от клиента, выполняет вычисления, после
этого формирует и отправляет её клиенту по сети с использованием протокола
HTTP.
Серверная часть веб приложения Depeche реализована исходя из особенностей архитектуры и соглашений фреймворка Ruby On Rails, которые были описаны ранее.
Имеются основные Контроллеры:
– application_controller — является базовым и генерируется вместе с приложением;
– static_pages_controller — отвечает за статические страницы приложения,
такие как: home, about, contact, chat. Их соответствующие методы являются пустыми (кроме chat), однако они необходимы для того, чтобы их можно было вызвать в Роутинге, а Вид в свою очередь предоставит пользователю необходимый
интерфейс;
– users_controller — выполняет основные операции над пользователем, такие как: создать, изменить;
– session_controller — отслеживает и изменяет состояние сессии;
– password_reset_controller — обеспечивает восстановление доступа к аккаунту через e-mail;
– conversation_controller — позволяет открывать и закрывать имеющиеся
диалоги в чате;
– message_controller — обеспечивает создание сообщения в конкретном
диалоге.
Каждый контроллер имеет ряд методов, предназначенных для реализации
нашей задачи, при выполнении которых взаимодействует с конкретной Моделью,
42
а Модель в свою очередь с базой данных. Важно отметить, что Модель не подозревает о существовании ни Контроллера, ни Вида, это необходимо чтобы разграничить обязанности. Всего их три:
– user (пользователь);
– conversation (диалог);
– message (сообщение);
Для того, чтобы сообщениями можно было обмениваться в режиме реального времени необходимо интегрировать веб-сокеты с помощью Action Cable, который предоставляет фреймворк Ruby on Rails.
Action Cable использует подход Publish-Subscribe для коммуникации между
сервером и множеством клиентов. Он относится к парадигме очереди сообщений,
когда отправители информации (publishers) посылают данные в абстрактный
класс получателей (subscribers), без указания отдельных получателей.
Соединения (connection) формируют основу взаимоотношения клиента с
сервером. Для каждого WebSocket, принимаемого сервером, на стороне сервера
будет инициализирован объект соединения. Этот объект становится родителем
для всех подписок на канал, которые создаются впоследствии. Само соединение
не работает с какой-либо определенной логикой приложения после аутентификации и авторизации. Клиент соединения WebSocket называется потребителем соединения (consumer). Отдельный пользователь создаст одну пару потребительсоединение на каждую вкладку браузера, окно или устройство, которые он использует.
Для работы Action Cable также необходимо сгенерировать канал, в нашем
приложении это conversation_channel, который в свою очередь инкапсулирует логическую единицу работы, схожей с той, что делает контроллер в обычном MVC.
43
3.2 Реализация клиентской части
Клиентская часть реализует пользовательский интерфейс, формирует запросы к серверу и обрабатывает ответы на него.
Интерфейс пользователя должен позволять:
– регистрировать новый аккаунт;
– сохранить настройки пользователя и восстанавливать их при последующих запусках приложения;
– входить и выходить из существующего аккаунта;
– обновлять информацию о пользователе;
– восстанавливать аккаунт с помощью e-mail;
– общаться с другими пользователями посредством текстовой и информации;
– просмотреть список пользователей;
– ограничивать возможности пользователя в соответствии с его правами;
За реализацию клиентской части ответственен Вид в архитектуре MVC.
3.3 Развертывание веб-приложения обмена текстовыми сообщениями
Клиентская часть реализует пользовательский интерфейс, формирует запросы к серверу и обрабатывает ответы на него.
Для того, чтобы приложение было доступно пользователям в интернет сети
его необходимо развернуть. Для этого была использована облачная платформа —
Heroku.
Heroku предоставляет весь необходимый функционал для нашего приложения и является условно бесплатной платформой.
Для полноценной работы приложения необходимо также подключить некоторые модули, благодаря которым приложение сможет корректно выполнять весь
разработанный функционал. Подключить необходимо следующие модули:
а) Heroku Postgres — база данных PostgreSQL, необходимая для хранения
информации;
44
б) SendGrid (SMPT) — отвечает за отправление на почту письма с инструкцией для восстановления пароля;
в) Heroku Redis — база данных, которая хранит все данные в памяти и осуществляет доступ к ним по ключу, необходима для хранения сообщений;
Приложения
app.herokuapp.com/
доступно
в
интернете
по
адресу
https://depeche-
45
4 ОПИСАНИЕ РАБОТЫ ВЕБ-ПРИЛОЖЕНИЯ ОБМЕНА ТЕКСТОВЫМИ
СООБЩЕНИЯМИ
Работа пользователя с приложением начинается со входа на сайт (рис. 16).
Рисунок 16 – Домашняя страница веб-приложения
Далее пользователь регистрируется в веб-приложении используя необходимые формы и поля, которые показаны на рисунке 17.
Рисунок 17 – Форма регистрации
46
Если пользователь введет некорректный e-mail, то высветится соответствующее предупреждение (рис. 18).
Рисунок 18 – Некорректный e-mail
Если пользователь при регистрации укажет e-mail, который уже занят другим пользователем в базе данных, или введет подтверждения пароля неверно –
будут показаны соответствующие ошибки (рис.19).
Рисунок 19 – Ошибки при регистрации
47
Если пользователь заполнил все поля корректно, при этом в базе данных отсутствует введенный e-mail, то пользователь будет успешно зарегистрирован и
автоматически войдет на сайт с помощью своей новой учетной записи (рис. 20).
Рисунок 20 – Успешная регистрация и вход в веб-приложение
После этого пользователь может выйти из аккаунта, зайти в настройки аккаунта, посетить страницу профиля (рис. 21).
Рисунок 21 – Операции с аккаунтом
Зарегистрировавшийся ранее пользователь может войти в существующую
учетную запись использую соответствую форму и страницу (рис. 22).
48
Рисунок 22 – Форма входа в учетную запись
При этом пользователь также может восстановить доступ к существующей
учетной записи, нажав на соответствующую кнопку, в том случае, если он забыл
пароль. Для этого необходимо ввести e-mail в соответствующую форму и следовать инструкции, которая будет выслана на указанную почту (рис. 23).
Рисунок 23 – Форма восстановления доступа через e-mail
Пользователь получит письмо с ссылкой, перейдя по которой сбросит старый пароль и сможет сделать новый, чтобы продолжить пользоваться вебприложением. Письмо с инструкцией по восстановлению пароля показано на рисунке 24.
49
Рисунок 24 – Письмо восстановления пароля
Если пользователь нажмет на «Сбросить пароль», то попадет на страницу, в
которой нужно будет ввести новый пароль (рис. 25).
Рисунок 25 – Форма восстановления пароля
Нажав на вкладку «Чат», пользователь перейдет на соответствующую страницу, на которой сможет выбрать собеседника из списка существующих. Список
пользователей продемонстрирован на рисунке 26.
50
Рисунок 26 – Список пользователей
После этого пользователь может отправить сообщение выбранному собеседнику (рис. 27).
Рисунок 27 – Отправленное сообщение
Собеседник на другой стороне сможет увидеть сообщение и ответить на него (рис. 28).
51
Рисунок 28 – Просмотр сообщения
В приложении реализована поддержка Markdown синтаксиса, что позволяет
общаться более разнообразно (рис. 29).
Рисунок 29 – Демонстрация работы Markdown синтаксиса
52
По мимо этого пользователи могут обмениваться небольшими документами, используя для этого соответствующую кнопку (рис. 30).
Рисунок 30 – Прикрепление документов
53
ЗАКЛЮЧЕНИЕ
Целью данной выпускной квалификационной работы является разработка и
реализация веб приложения для обмена текстовыми сообщениями.
В работе был проведен анализ приложений-мессенджеров и текущих
средств отправки сообщений. Помимо главной задачи удалось спроектировать
следующие задачи:
а) полноценную аутентификацию пользователей;
б) возможность восстановления аккаунта;
в) возможность обновления пользовательской информации;
Была спроектирована система регистрации, чтобы она была эффективной,
были определены следующие столбы, которым нужно уделить внимание: человечность, потраченное время, корректность, безопасность, пароль.
Были описаны среды разработки и выбрана самая оптимальная и практичная для заданной цели.
Решены следующие задачи:
1. Проанализирована предметная область;
2. Изучена технология создания веб приложений;
3. Спроектирована система;
Приложение разрабатывалось с учетом возможности расширения существующего функционала.
В результате работы был получен полностью готовый для использования
проект приложения. Автор полагает, что полностью справился с поставленной задачей.
54
СПИСОК ИСПОЛЬЗУЕМОЙ ЛИТЕРАТУРЫ
1. Хартл М. «Ruby on Rails Туториал: Обучение Веб Разработке с Rails»,
2017.
2. Флэнаган Д., Мацумото Ю. «Язык программирования Ruby», 2009.
3. А.Н. Ковязин, С.М. Востриков. «Мир InterBase. Архитектура, администрирование и разработка приложений баз данных», 2002.
4. Ruby On Rails Guides — [Электронный ресурс] — режим доступа:
http://guides.rubyonrails.org, свободный.
5. HTML – Википедия — [Электронный ресурс] – режим доступа:
https://ru.wikipedia.org/wiki/HTML, свободный.
6. htmlbook.ru | Для тех, кто делает сайты — [Электронный ресурс] – режим
доступа: http://htmlbook.ru/, свободный.
7. CSS – Википедия — [Электронный ресурс] – режим доступа:
https://ru.wikipedia.org/wiki/CSS, свободный.
8. JavaScript – Википедия — [Электронный ресурс] – режим доступа:
https://ru.wikipedia.org/wiki/JavaScript, свободный.
9. Современный учебник JavaScript — [Электронный ресурс] – режим доступа: https://learn.javascript.ru/, свободный.
10. PostgreSQL – Википедия — [Электронный ресурс] – режим доступа:
https://ru.wikipedia.org/wiki/PostgreSQL, свободный.
11. nginx — [Электронный ресурс] – режим доступа: http://nginx.org/ru/, свободный.
12. HTTP – Википедия — [Электронный ресурс] – режим доступа:
https://ru.wikipedia.org/wiki/HTTP, свободный.
13. HTTPS – Википедия — [Электронный ресурс] – режим доступа:
https://ru.wikipedia.org/wiki/HTTPS, свободный.
14. SSL – Википедия — [Электронный ресурс] – режим доступа:
https://ru.wikipedia.org/wiki/SSL, свободный.
55
15. TLS – Википедия — [Электронный ресурс] – режим доступа:
https://ru.wikipedia.org/wiki/TLS, свободный.
16. OpenSSL – Википедия — [Электронный ресурс] – режим доступа:
https://ru.wikipedia.org/wiki/OpenSSL, свободный.
17) JSON – Википедия — [Электронный ресурс] – режим доступа:
https://ru.wikipedia.org/wiki/JSON, свободный.
18) JQuery – Википедия — [Электронный ресурс] – режим доступа:
https://ru.wikipedia.org/wiki/JQuery, свободный.
56
ПРИЛОЖЕНИЕ А (ОБЯЗАТЕЛЬНОЕ)
Фрагменты кода программы
Содержимое файла depeche/app/assets/js/channels/conversation.js
App.conversation = App.cable.subscriptions.create('ConversationChannel', {
connected: function() {},
disconnected: function() {},
received: function(data) {
var conversation, height, messages_list;
conversation
=
$('#conversations-list').find('[data-conversation-id=\''
ta['conversation_id'] + '\']');
conversation.find('.messages-list').find('ul').append(data['message']);
messages_list = conversation.find('.messages-list');
height = messages_list[0].scrollHeight;
messages_list.scrollTop(height);
},
speak: function(message) {
return this.perform('speak', {
message: message
});
}
});
$(document).on('submit', '.new_message', function(e) {
var values;
e.preventDefault();
values = $(this).serializeArray();
App.conversation.speak(values);
$(this).trigger('reset');
+
da-
57
});
Содержимое файла depeche/app/assets/js/application.js
(function() {
$(document).on('click', '.toggle-window', function(e) {
e.preventDefault();
var panel = $(this).parent().parent();
var messages_list = panel.find('.messages-list');
panel.find('.panel-body').toggle();
panel.attr('class', 'panel panel-default');
if (panel.find('.panel-body').is(':visible')) {
var height = messages_list[0].scrollHeight;
messages_list.scrollTop(height);
}
});
})();
Содержимое файла depeche/app/assets/js/cable.js
(function() {
this.App || (this.App = {});
App.cable = ActionCable.createConsumer();
}).call(this);
Содержимое файла depeche/app/stylesheets/custom.scss
@import "bootstrap-sprockets";
58
@import "bootstrap";
/* mixins, variables, etc. */
$gray-medium-light: #eaeaea;
/* universal */
body {
padding-top: 60px;
}
section {
overflow: auto;
}
textarea {
resize: vertical;
}
.center {
text-align: center;
h1 {
margin-bottom: 10px;
}
}
/* typography */
h1, h2, h3, h4, h5, h6 {
59
line-height: 1;
}
h1 {
font-size: 3em;
letter-spacing: -2px;
margin-bottom: 30px;
text-align: center;
}
h2 {
font-size: 1.2em;
letter-spacing: -1px;
margin-bottom: 30px;
text-align: center;
font-weight: normal;
color: $gray-light;
}
p{
font-size: 1.1em;
line-height: 1.7em;
}
/* header */
#logo {
float: left;
margin-right: 10px;
font-size: 1.7em;
60
color: white;
text-transform: uppercase;
letter-spacing: -1px;
padding-top: 9px;
font-weight: bold;
&:hover {
color: white;
text-decoration: none;
}
}
/* footer */
footer {
margin-top: 45px;
padding-top: 5px;
border-top: 1px solid $gray-medium-light;
color: $gray-light;
a{
color: $gray;
&:hover {
color: $gray-darker;
}
}
small {
float: left;
}
ul {
float: right;
list-style: none;
61
li {
float: left;
margin-left: 15px;
}
}
}
@mixin box_sizing {
-moz-box-sizing: border-box;
-webkit-box-sizing: border-box;
box-sizing: border-box;
}
/* miscellaneous */
.debug_dump {
clear: both;
float: left;
width: 100%;
margin-top: 45px;
@include box_sizing;
}
/* sidebar */
aside {
section.user_info {
margin-top: 20px;
}
section {
62
padding: 10px 0;
margin-top: 20px;
&:first-child {
border: 0;
padding-top: 0;
}
span {
display: block;
margin-bottom: 3px;
line-height: 1;
}
h1 {
font-size: 1.4em;
text-align: left;
letter-spacing: -1px;
margin-bottom: 3px;
margin-top: 0px;
}
}
}
.gravatar {
float: left;
margin-right: 10px;
}
.gravatar_edit {
margin-top: 15px;
}
63
/* forms */
input, textarea, select, .uneditable-input {
border: 1px solid #bbb;
width: 100%;
margin-bottom: 15px;
@include box_sizing;
}
input {
height: auto !important;
}
#error_explanation {
color: red;
ul {
color: red;
margin: 0 0 30px 0;
}
}
.field_with_errors {
@extend .has-error;
.form-control {
color: $state-danger-text;
}
}
.checkbox {
margin-top: -10px;
64
margin-bottom: 10px;
span {
margin-left: 20px;
font-weight: normal;
}
}
#session_remember_me {
width: auto;
margin-left: 0;
}
/* Users index */
.users {
list-style: none;
margin: 0;
li {
overflow: auto;
padding: 10px 0;
border-bottom: 1px solid $gray-lighter;
}
}
ul {
padding-left: 0px;
list-style: none;
}
.messages-list {
65
max-height: 200px;
overflow-y: auto;
overflow-x: hidden;
}
.message-sent {
position: relative;
background-color: #D9EDF7;
border-color: #BCE8F1;
margin: 5px 20px;
padding: 10px;
float: right;
}
.message-received {
background-color: #F1F0F0;
border-color: #EEEEEE;
margin: 5px 20px;
padding: 10px;
float: left;
}
Содержимое файла depeche/app/channels/application_cable/connection.rb
module ApplicationCable
class Connection < ActionCable::Connection::Base
identified_by :current_user
def connect
self.current_user = find_verified_user
end
protected
66
def find_verified_user
if (current_user = User.find_by(id: cookies.signed[:user_id]))
current_user
else
reject_unauthorized_connection
end
end
end
end
Содержимое файла depeche/app/channels/conversation_channel.rb
class ConversationChannel < ApplicationCable::Channel
def subscribed
stream_from "conversations-#{current_user.id}"
end
def unsubscribed
stop_all_streams
end
def speak(data)
message_params = data['message'].each_with_object({}) do |el, hash|
hash[el.values.first] = el.values.last
end
Message.create(message_params)
end
end
Содержимое файла depeche/app/controllers/conversation_controller.rb
class ConversationsController < ApplicationController
def create
@conversation = Conversation.get(current_user.id, params[:user_id])
67
add_to_conversations unless conversated?
respond_to do |format|
format.js
end
end
def close
@conversation = Conversation.find(params[:id])
session[:conversations].delete(@conversation.id)
respond_to do |format|
format.js
end
end
private
def add_to_conversations
session[:conversations] ||= []
session[:conversations] << @conversation.id
end
def conversated?
session[:conversations].include?(@conversation.id)
end
end
Содержимое файла depeche/app/controllers/messages_controller.rb
class MessagesController < ApplicationController
def create
68
@conversation
=
Conversa-
tion.includes(:recipient).find(params[:conversation_id])
@message = @conversation.messages.create(message_params)
respond_to do |format|
format.js
end
end
private
def message_params
params.require(:message).permit(:user_id, :body)
end
end
Содержимое файла depeche/app/controllers/password_resets_controller.rb
class PasswordResetsController < ApplicationController
before_action :get_user, only: [:edit, :update]
before_action :valid_user, only: [:edit, :update]
before_action :check_expiration, only: [:edit, :update] # Case (1)
def new
end
def create
@user = User.find_by(email: params[:password_reset][:email].downcase)
if @user
@user.create_reset_digest
@user.send_password_reset_email
69
flash[:info] = "Email sent with password reset instructions"
redirect_to root_url
else
flash.now[:danger] = "Email address not found"
render 'new'
end
end
def edit
end
def update
if params[:user][:password].empty? # Case (3)
@user.errors.add(:password, "can't be empty")
render 'edit'
elsif @user.update_attributes(user_params) # Case (4)
log_in @user
@user.update_attribute(:reset_digest, nil)
flash[:success] = "Password has been reset."
redirect_to @user
else
render 'edit' # Case (2)
end
end
private
def user_params
params.require(:user).permit(:password, :password_confirmation)
end
70
# Before filters
def get_user
@user = User.find_by(email: params[:email])
end
# Confirms a valid user.
def valid_user
unless (@user && @user.authenticated?(:reset, params[:id]))
redirect_to root_url
end
end
# Checks expiration of reset token.
def check_expiration
if @user.password_reset_expired?
flash[:danger] = "Password reset has expired."
redirect_to new_password_reset_url
end
end
end
Содержимое файла depeche/app/controllers/sessions_controller.rb
class SessionsController < ApplicationController
def new
end
def create
@user = User.find_by(email: params[:session][:email].downcase)
71
if @user && @user.authenticate(params[:session][:password])
log_in @user
params[:session][:remember_me] == '1' ? remember(@user) : forget(@user)
cookies.signed[:user_id] = @user.id
redirect_back_or @user
else
flash.now[:danger] = 'Invalid email/password combination'
render 'new'
end
end
def destroy
log_out if logged_in?
redirect_to root_url
end
end
Содержимое файла depeche/app/controllers/static_pages_controller.rb
class StaticPagesController < ApplicationController
def home
end
def help
end
def about
end
def contact
72
end
def index
session[:conversations] ||= []
@users = User.all.where.not(id: current_user)
@conversations = Conversation.includes(:recipient, :messages)
.find(session[:conversations])
end
end
Содержимое файла depeche/app/controllers/users_controller.rb
class UsersController < ApplicationController
before_action :logged_in_user, only: [:index, :edit, :update, :destroy]
before_action :correct_user, only: [:edit, :update]
before_action :admin_user, only: :destroy
def index
@users = User.paginate(page: params[:page])
end
def show
@user = User.find(params[:id])
end
def new
@user = User.new
end
def create
@user = User.new(user_params)
73
if @user.save
log_in @user
flash[:success] = "Welcome to the Sample App!"
cookies.signed[:user_id] = @user.id
redirect_to @user
else
render 'new'
end
end
def edit
@user = User.find(params[:id])
end
def update
@user = User.find(params[:id])
if @user.update_attributes(user_params)
flash[:success] = "Profile updated"
redirect_to @user
else
render 'edit'
end
end
def destroy
User.find(params[:id]).destroy
flash[:success] = "User deleted"
redirect_to users_url
end
74
private
def user_params
params.require(:user).permit(:name, :email, :password,
:password_confirmation)
end
# Before filters
# Confirms a logged-in user
def logged_in_user
unless logged_in?
store_location
flash[:danger] = "Please log in"
redirect_to login_url
end
end
# Confirms the correct user.
def correct_user
@user = User.find(params[:id])
redirect_to(root_url) unless current_user?(@user)
end
# Confirms an admin user.
def admin_user
redirect_to(root_url) unless current_user.admin?
end
end
75
Содержимое файла depeche/app/helpers/application_helper.rb
module ApplicationHelper
# Returns the full title on a per-page basis.
def full_title(page_title = '')
base_title = "Depeche"
if page_title.empty?
base_title
else
page_title + " | " + base_title
end
end
end
Содержимое файла depeche/app/helpers/messages_helper.rb
module MessagesHelper
# Returns HTML from input text using GitHub-flavored Markdown.
def markdown_to_html(text)
Kramdown::Document.new(text, input: 'GFM').to_html
end
end
Содержимое файла depeche/app/helpers/session_helper.rb
module SessionsHelper
# Logs in the given user
76
def log_in(user)
session[:user_id] = user.id
end
def remember(user)
user.remember
cookies.permanent.signed[:user_id] = user.id
cookies.permanent[:remember_token] = user.remember_token
end
# Remembers a user in a persistent session
def current_user?(user)
user == current_user
end
# Returns the current logged-in user (if any)
def current_user
if (user_id = session[:user_id])
@current_user ||= User.find_by(id: user_id)
elsif (user_id = cookies.signed[:user_id])
user = User.find_by(id: user_id)
if user && user.authenticated?(:remember, cookies[:remember_token])
log_in user
@current_user = user
end
end
end
def logged_in?
!current_user.nil?
77
end
# Forgets a persistent session
def forget(user)
user.forget
cookies.delete(:user_id)
cookies.delete(:remember_token)
end
# Logs out the current user
def log_out
forget(current_user)
session.delete(:user_id)
@current_user = nil
end
# Redirects to stored location (or to the default).
def redirect_back_or(default)
redirect_to(session[:forwarding_url] || default)
session.delete(:forwarding_url)
end
# Stores the URL trying to be accessed.
def store_location
session[:forwarding_url] = request.original_url if request.get?
end
end
Содержимое файла depeche/app/helpers/users_helper.rb
module UsersHelper
78
# Returns the Gravatar for the given user.
def gravatar_for(user, size: 80)
gravatar_id = Digest::MD5::hexdigest(user.email.downcase)
gravatar_url = "https://secure.gravatar.com/avatar/#{gravatar_id}?s=#{size}"
image_tag(gravatar_url, alt: user.name, class: "gravatar")
end
Содержимое файла depeche/app/jobs/message_broadcast_job.rb
class MessageBroadcastJob < ApplicationJob
queue_as :default
def perform(message)
sender = message.user
recipient = message.conversation.opposed_user(sender)
broadcast_to_sender(sender, message)
broadcast_to_recipient(recipient, message)
end
private
def broadcast_to_sender(user, message)
ActionCable.server.broadcast(
"conversations-#{user.id}",
message: render_message(message, user),
conversation_id: message.conversation_id
)
end
def broadcast_to_recipient(user, message)
ActionCable.server.broadcast(
79
"conversations-#{user.id}",
message: render_message(message, user),
conversation_id: message.conversation_id
)
end
def render_message(message, user)
ApplicationController.render(
partial: 'messages/message',
locals: { message: message, user: user }
)
end
end
Содержимое файла depeche/app/mailer/user_mailer.rb
class UserMailer < ApplicationMailer
def password_reset(user)
@user = user
mail to: user.email, subject: "Password reset"
end
end
Содержимое файла depeche/app/models/conversation.rb
class Conversation < ApplicationRecord
has_many :messages, dependent: :destroy
belongs_to :sender, foreign_key: :sender_id, class_name: User
belongs_to :recipient, foreign_key: :recipient_id, class_name: User
80
validates :sender_id, uniqueness: { scope: :recipient_id }
scope :between, -> (sender_id, recipient_id) do
where(sender_id: sender_id, recipient_id: recipient_id).or(
where(sender_id: recipient_id, recipient_id: sender_id)
)
end
def self.get(sender_id, recipient_id)
conversation = between(sender_id, recipient_id).first
return conversation if conversation.present?
create(sender_id: sender_id, recipient_id: recipient_id)
end
def opposed_user(user)
user == recipient ? sender : recipient
end
end
Содержимое файла depeche/app/models/user.rb
class User < ApplicationRecord
has_many :messages
has_many :conversations, foreign_key: :sender_id
attr_accessor :remember_token, :reset_token
before_save { self.email = email.downcase }
validates :name, presence: true, length: { maximum: 50 }
VALID_EMAIL_REGEX = /\A[\w+\-.][email protected][a-z\d\-.]+\.[a-z]+\z/i
validates :email, presence: true, length: { maximum: 255 },
format: { with: VALID_EMAIL_REGEX },
81
uniqueness: { case_sensitive: false }
has_secure_password
validates :password, presence: true, length: { minimum: 6 }, allow_nil: true
# Returns the hash digest of the given string.
def User.digest(string)
cost = ActiveModel::SecurePassword.min_cost ? BCrypt::Engine::MIN_COST :
BCrypt::Engine.cost
BCrypt::Password.create(string, cost: cost)
end
# Returns a random token.
def User.new_token
SecureRandom.urlsafe_base64
end
# Remembers a user in the database for use in persistent sessions
def remember
self.remember_token = User.new_token
update_attribute(:remember_digest, User.digest(remember_token))
end
# Returns true if the given token matches the digest.
def authenticated?(attribute, token)
digest = send("#{attribute}_digest")
return false if digest.nil?
BCrypt::Password.new(digest).is_password?(token)
end
# Forgets a user
82
def forget
update_attribute(:remember_digest, nil)
end
# Sets the password reset attributes.
def create_reset_digest
self.reset_token = User.new_token
# update_attribute(:reset_digest, User.digest(reset_token))
# update_attribute(:reset_sent_at, Time.zone.now)
update_columns(reset_digest:
User.digest(reset_token),
Time.zone.now)
end
# Sends password reset email.
def send_password_reset_email
UserMailer.password_reset(self).deliver_now
end
# Returns true if a password reset has expired
def password_reset_expired?
reset_sent_at < 2.hours.ago
end
end
Содержимое файла depeche/app/models/message.rb
class Message < ApplicationRecord
belongs_to :user
belongs_to :conversation
after_create_commit { MessageBroadcastJob.perform_later(self) }
reset_sent_at:
83
end
Содержимое
файла
depeche/app/views/conversations/_conversation_content.html.erb
<% messages.each do |message| %>
<%= render message, user: user %>
<% end %>
Содержимое файла depeche/app/views/_conversation.html.erb
<li>
<div class="panel panel-default" data-conversation-id="<%= conversation.id
%>">
<div class="panel-heading">
<%= link_to conversation.opposed_user(user).email, '', class: 'toggle-window' %>
<%= link_to "close", close_conversation_path(conversation), class: "btn btndefault btn-xs pull-right", remote: true, method: :post %>
</div>
<div class="panel-body" style="display: none;">
<div class="messages-list">
<ul>
<%=
render
'conversations/conversation_content',
messages:
conversa-
tion.messages, user: user %>
</ul>
</div>
<form class="new_message">
<input name="conversation_id" type="hidden" value="<%= conversation.id
%>">
<input name="user_id" type="hidden" value="<%= user.id %>">
84
<textarea name="body" class="form-control"></textarea>
<input type="submit" class="btn btn-success" value="Send">
</form>
</div>
</div>
</li>
Содержимое файла depeche/app/views/conversation/close.js.erb
var conversations = $('#conversations-list');
var
conversation
=
conversations.find("[data-conversation-id='"
+
"<%=
+
"<%=
@conversation.id %>" + "']");
conversation.parent().remove();
Содержимое файла depeche/app/views/conversation.create.js.erb
var conversations = $('#conversations-list');
var
conversation
=
conversations.find("[data-conversation-id='"
@conversation.id %>" + "']");
if (conversation.length !== 1) {
conversations.append("<%= j(render 'conversations/conversation', conversation:
@conversation, user: current_user) %>");
conversation
=
conversations.find("[data-conversation-id='"
@conversation.id %>" + "']");
}
conversation.find('.panel-body').show();
var messages_list = conversation.find('.messages-list');
var height = messages_list[0].scrollHeight;
+
"<%=
85
messages_list.scrollTop(height);
Содержимое файла depeche/app/views/layouts/_footer.html.erb
<footer class="footer">
<small>
Depeche App by <a href="http://www.github.com/apatchenkov">Apatchenkov
Danila</a>
</small>
<nav>
<ul>
<li><%= link_to "About", about_path %></li>
<li><%= link_to "Contact", contact_path %></li>
</ul>
</nav>
</footer>
Содержимое файла depeche/app/views/layouts/_header.html.erb
<header class="navbar navbar-fixed-top navbar-inverse">
<div class="container">
<%= link_to "depeche", root_path, id: "logo" %>
<nav>
<ul class="nav navbar-nav navbar-right">
<li><%= link_to "Home", root_path %></li>
<% if logged_in? %>
<li><%= link_to "Users", users_path %></li>
<li><%= link_to "Chat", chat_path %></li>
<li class="dropdown">
<a href="#" class="dropdown-toggle" data-toggle="dropdown">
Account <b class="caret"></b>
</a>
86
<ul class="dropdown-menu">
<li><%= link_to "Profile", current_user %></li>
<li><%= link_to "Settings", edit_user_path(current_user) %></li>
<li class="divider"></li>
<li>
<%= link_to "Log out", logout_path, method: :delete %>
</li>
</ul>
</li>
<% else %>
<li><%= link_to "Log in", login_path %></li>
<% end %>
</ul>
</nav>
</div>
</header>
Содержимое файла depeche/app/views/layouts/application.html.erb
<header class="navbar navbar-fixed-top navbar-inverse">
<div class="container">
<%= link_to "depeche", root_path, id: "logo" %>
<nav>
<ul class="nav navbar-nav navbar-right">
<li><%= link_to "Home", root_path %></li>
<% if logged_in? %>
<li><%= link_to "Users", users_path %></li>
<li><%= link_to "Chat", chat_path %></li>
<li class="dropdown">
<a href="#" class="dropdown-toggle" data-toggle="dropdown">
Account <b class="caret"></b>
87
</a>
<ul class="dropdown-menu">
<li><%= link_to "Profile", current_user %></li>
<li><%= link_to "Settings", edit_user_path(current_user) %></li>
<li class="divider"></li>
<li>
<%= link_to "Log out", logout_path, method: :delete %>
</li>
</ul>
</li>
<% else %>
<li><%= link_to "Log in", login_path %></li>
<% end %>
</ul>
</nav>
</div>
</header>
Содержимое файла depeche/app/views/layouts/mailer.html.erb
<!DOCTYPE html>
<html>
<head>
<meta http-equiv="Content-Type" content="text/html; charset=utf-8" />
<style>
/* Email styles need to be inline */
</style>
</head>
<body>
<%= yield %>
88
</body>
</html>
Содержимое файла depeche/app/views/layouts/mailer.text.erb
<%= yield %>
Содержимое файла depeche/app/views/messages/_message.html.erb
<li>
<div class="row">
<div class="<%= user.id == message.user_id ? 'message-sent' : 'messagereceived' %>">
<%= sanitize markdown_to_html(message.body) %>
</div>
</div>
</li>
Содержимое файла depeche/app/views/messages/create.js.erb
var conversation = $('#conversations-list').find("[data-conversation-id='" + "<%=
@conversation.id %>" + "']");
conversation.find('.messages-list').find('ul').append("<%=
j(render
es/message', message: @message, user: current_user) %>");
conversation.find('textarea').val('');
Содержимое файла depeche/app/views/password_resets/edit.html.erb
<% provide(:title, 'Reset password') %>
<h1>Reset password</h1>
<div class="row">
'messag-
89
<div class="col-md-6 col-md-offset-3">
<%= form_for(@user, url: password_reset_path(params[:id])) do |f| %>
<%= render 'shared/error_messages' %>
<%= hidden_field_tag :email, @user.email %>
<%= f.label :password %>
<%= f.password_field :password, class: 'form-control' %>
<%= f.label :password_confirmation, "Confirmation" %>
<%= f.password_field :password_confirmation, class: 'form-control' %>
<%= f.submit "Update password", class: "btn btn-primary" %>
<% end %>
</div>
</div>
Содержимое файла depeche/app/views/passwprd_resets/new.html.erb
<% provide(:title, "Forgot password") %>
<h1>Forgot password</h1>
<div class="row">
<div class="col-md-6 col-md-offset-3">
<%= form_for(:password_reset, url: password_resets_path) do |f| %>
<%= f.label :email %>
<%= f.email_field :email, class: 'form-control' %>
<%= f.submit "Submit", class: "btn btn-primary" %>
<% end %>
</div>
90
</div>
Содержимое файла depeche/app/views/sessions/new.html.erb
<% provide(:title, "Log in") %>
<h1>Log in</h1>
<div class="row">
<div class="col-md-6 col-md-offset-3">
<%= form_for(:session, url: login_path) do |f| %>
<%= f.label :email %>
<%= f.email_field :email, class: 'form-control' %>
<%= f.label :password %>
<%= link_to "(forgot password)", new_password_reset_path %>
<%= f.password_field :password, class: 'form-control' %>
<%= f.label :remember_me, class: "checkbox inline" do %>
<%= f.check_box :remember_me %>
<span>Remember me on this computer</span>
<% end %>
<%= f.submit "Log in", class: "btn btn-primary" %>
<% end %>
<p>New user? <%= link_to "Sign up now!", signup_path %></p>
</div>
</div>
Содержимое файла depeche/app/views/shared/_error_messages.html.erb
91
<% if @user.errors.any? %>
<div id="error_explanation">
<div class="alert alert-danger">
The form contains <%= pluralize(@user.errors.count, "error") %>.
</div>
<ul>
<% @user.errors.full_messages.each do |msg| %>
<li><%= msg %></li>
<% end %>
</ul>
</div>
<% end %>
Содержимое файла depeche/app/views/static_pages/about.html.erb
<% provide(:title, "About") %>
<h1>About</h1>
<p>
<em>Depeche</em> is a
<a href="https://en.wikipedia.org/wiki/Web_application">web application</a>
created by <a href="http://github.com/apatchenkov">Apatchenkov Danila</a>
for educational purposes.
</p>
Содержимое файла depeche/app/views/static_pages/contact.html.erb
<% provide(:title, "Contact") %>
<h1>Contact</h1>
<p>
<a href="mailto:[email protected]">E-mail</a>
and
92
<a href="http://www.github.com/apatchenkov">Github</a>
</p>
Содержимое файла depeche/app/views/static_pages/help.html.erb
<% provide(:title, "Help") %>
<h1>Help</h1>
<p>
Get help on the Ruby on Rails Tutorial at the
<a href="http://www.railstutorial.org/help">Rails Tutorial help
page</a>.
To get help on this sample app, see the
<a href="http://www.railstutorial.org/book"><em>Ruby on Rails
Tutorial</em> book</a>.
</p>
Содержимое файла depeche/app/views/static_pages/home.html.erb
<div class="center jumbotron">
<h1>Welcome to the Depeche App</h1>
<h2>
Try it!
</h2>
<%= link_to "Sign up now!", signup_path, class: "btn btn-lg btn-primary" %>
<h2>
</div>
Содержимое файла depeche/app/views/static_pages/index.html.erb
93
<div class="row">
<div class="col-md-9">
<ul id="conversations-list">
<% @conversations.each do |conversation| %>
<%= render 'conversations/conversation', conversation: conversation, user: current_user %>
<% end %>
</ul>
</div>
<div class="col-md-3">
<div class="panel panel-primary">
<div class="panel-heading">
<h3 class="panel-title">User list</h3>
</div>
<div class="panel-body">
<ul>
<% @users.each do |user| %>
<li><%= link_to user.email, conversations_path(user_id: user), remote: true,
method: :post %></li>
<% end %>
</ul>
</div>
</div>
</div>
</div>
Содержимое файла depeche/app/views/user_mailer/password_reset.html.erb
<h1>Password reset</h1>
94
<p>To reset your password click the link below:</p>
<%= link_to "Reset password", edit_password_reset_url(@user.reset_token,
email: @user.email) %>
<p>This link will expire in two hours.</p>
<p>
If you did not request your password to be reset, please ignore this email and
your password will stay as it is.
</p>
Содержимое файла depeche/app/views/user_mailer/password_reset.txt.erb
To reset your password click the link below:
<%= edit_password_reset_url(@user.reset_token, email: @user.email) %>
This link will expire in two hours.
If you did not request your password to be reset, please ignore this email and
your password will stay as it is.
Содержимое файла depeche/app/views/users/_form.html.erb
<%= form_for(@user) do |f| %>
<%= render 'shared/error_messages', object: @user %>
<%= f.label :name %>
<%= f.text_field :name, class: 'form-control' %>
<%= f.label :email %>
95
<%= f.email_field :email, class: 'form-control' %>
<%= f.label :password %>
<%= f.password_field :password, class: 'form-control' %>
<%= f.label :password_confirmation %>
<%= f.password_field :password_confirmation, class: 'form-control' %>
<%= f.submit yield(:button_text), class: "btn btn-primary" %>
<% end %>
Содержимое файла depeche/app/views/users/_user.html.erb
<li>
<%= gravatar_for user, size: 50 %>
<%= link_to user.name, user %>
<% if current_user.admin? && !current_user?(user) %>
| <%= link_to "delete", user, method: :delete,
data: { confirm: "You sure?" } %>
<% end %>
</li>
Содержимое файла depeche/app/views/users/edit.html.erb
<% provide(:title, 'Edit user') %>
<% provide(:button_text, 'Save changes') %>
<h1>Update your profile</h1>
<div class="row">
<div class="col-md-6 col-md-offset-3">
<%= render 'form' %>
<div class="gravatar_edit">
<%= gravatar_for @user %>
96
<a href="http://gravatar.com/emails" target="_blank">Change</a>
</div>
</div>
</div>
Содержимое файла depeche/app/views/users/index.html.erb
<% provide(:title, 'All users') %>
<h1>All users</h1>
<%= will_paginate %>
<ul class="users">
<%= render @users %>
</ul>
<%= will_paginate %>
Содержимое файла depeche/app/views/users/new.html.erb
<% provide(:title, 'Sign up') %>
<% provide(:button_text, 'Create my account') %>
<h1>Sign up</h1>
<div class="row">
<div class="col-md-6 col-md-offset-3">
<%= render 'form' %>
</div>
</div>
Содержимое файла depeche/app/views/users/show.html.erb
<% provide(:title, @user.name) %>
<div class="row">
97
<aside class="col-md-4">
<section class="user_info">
<h1>
<%= gravatar_for @user %>
<%= @user.name %>
</h1>
</section>
</aside>
</div>
Содержимое файла depeche/app/config/routes.rb
Rails.application.routes.draw do
root 'static_pages#home'
get '/help', to: 'static_pages#help'
get '/about', to: 'static_pages#about'
get '/contact', to: 'static_pages#contact'
get '/chat', to: 'static_pages#index'
get '/signup', to: 'users#new'
get '/login', to: 'sessions#new'
post '/login', to: 'sessions#create'
delete '/logout', to: 'sessions#destroy'
resources :users
resources :password_resets, only: [:new, :create, :edit, :update]
resources :conversations, only: [:create] do
member do
post :close
end
resources :messages, only: [:create]
end
end
98
Содержимое файла depeche/app/config/cable.yml
development:
adapter: async
test:
adapter: async
production:
adapter: redis
url:
re-
dis://h:pf[email protected]e
c2-34-237-97-204.compute-1.amazonaws.com:35919
channel_prefix: depeche-ror_production
Содержимое файла depeche/app/db/schema.rb
ActiveRecord::Schema.define(version: 20180308001106) do
create_table "conversations", force: :cascade do |t|
t.integer "recipient_id"
t.integer "sender_id"
t.datetime "created_at", null: false
t.datetime "updated_at", null: false
t.index
["recipient_id",
"sender_id"],
name:
dex_conversations_on_recipient_id_and_sender_id", unique: true
t.index ["recipient_id"], name: "index_conversations_on_recipient_id"
t.index ["sender_id"], name: "index_conversations_on_sender_id"
end
"in-
99
create_table "messages", force: :cascade do |t|
t.text "body"
t.integer "user_id"
t.integer "conversation_id"
t.datetime "created_at", null: false
t.datetime "updated_at", null: false
t.index ["conversation_id"], name: "index_messages_on_conversation_id"
t.index ["user_id"], name: "index_messages_on_user_id"
end
create_table "users", force: :cascade do |t|
t.string "name"
t.string "email"
t.datetime "created_at", null: false
t.datetime "updated_at", null: false
t.string "password_digest"
t.string "remember_digest"
t.boolean "admin", default: false
t.string "reset_digest"
t.datetime "reset_sent_at"
t.index ["email"], name: "index_users_on_email", unique: true
end
end
Содержимое файла depeche/gemfile
source 'https://rubygems.org'
gem 'rails', '5.1.4'
100
gem 'bcrypt', '3.1.11'
gem 'faker', '1.7.3'
gem 'will_paginate', '3.1.6'
gem 'bootstrap-will_paginate', '1.0.0'
gem 'bootstrap-sass', '3.3.7'
gem 'puma', '3.9.1'
gem 'sass-rails', '5.0.6'
gem 'uglifier', '3.2.0'
gem 'coffee-rails', '4.2.2'
gem 'jquery-rails', '4.3.1'
gem 'turbolinks', '5.0.1'
gem 'jbuilder', '2.7.0'
gem 'redis', '~> 3.0'
gem 'kramdown', '1.12.0'
group :development, :test do
gem 'sqlite3', '1.3.13'
gem 'byebug', '9.0.6', platform: :mri
end
group :development do
gem 'web-console', '3.5.1'
gem 'listen', '3.1.5'
gem 'spring', '2.0.2'
gem 'spring-watcher-listen', '2.0.1'
end
group :test do
gem 'rails-controller-testing', '1.0.2'
gem 'minitest-reporters', '1.1.14'
101
gem 'guard', '2.13.0'
gem 'guard-minitest', '2.4.4'
end
group :production do
gem 'pg', '0.18.4'
gem 'redis', '~> 3.0'
end
# Windows does not include zoneinfo files, so bundle the tzinfo-data gem
gem 'tzinfo-data', platforms: [:mingw, :mswin, :x64_mingw, :jruby]
1/--страниц
Пожаловаться на содержимое документа