Планирование

Приступая к этому проекту, у меня появилось повышенное чувство уверенности, которого у меня еще не было. Наконец-то я почувствовал, что избавился от синдрома самозванца, который преследовал меня на протяжении большей части курса. Хотя я не сомневаюсь, что на моем пути к разработке программного обеспечения еще многому предстоит научиться (это одна из многочисленных причин, по которым я решил заняться этой областью), уверенность казалась полезной кульминацией моих многих часов обучения, стресса и практики. . Фуф!

Когда я начал процесс планирования этого проекта, я пробежался по ряду потенциальных идей. Я знал, что хочу, чтобы тема была чем-то, чем я был увлечен, так как это сделало бы сборку более приятной. Я сузил свой список и решил реализовать одну из своих первых идей — отслеживание исчезающих видов. Идея заключалась в том, что пользователь мог зарегистрироваться или войти в систему, чтобы получить доступ к основным функциям веб-приложения — извлечению данных из API онлайн-системы охраны окружающей среды (ECOS) о видах, находящихся под угрозой исчезновения во всем мире.

Функциональность приложения

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

Когда пользователь нажимает кнопку «Сохранить виды на панели инструментов», запрос POST отправляется на мой серверный интерфейс Rails API через запрос на выборку. Данные о видах, которые были получены из ECOS API, затем сохраняются в базе данных моего внутреннего API, а затем извлекаются оттуда с помощью запроса GET для отображения на странице панели инструментов соответствующего пользователя. Пользователь также может удалить вид со своей панели инструментов с помощью кнопки, отображаемой на карточке вида, которая запускает запрос DELETE на серверную часть.

Бэкенд-дизайн

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

Чтобы создать свой бэкэнд, я использовал команду «rails new». Затем я использовал генератор ресурсов для создания моделей, контроллеров, маршрутов и миграций для User и AnimalCard. Я создал отношения User has_many AnimalsCards и AnimalCard created_to User. Кроме того, я создал сериализаторы для User и AnimalCard, чтобы мне было проще просматривать и получать доступ к ассоциациям (через JSON) из внешнего интерфейса.

Для аутентификации пользователя я решил использовать HTTPOnly Cookies, чтобы пользователь мог оставаться в системе при обновлении страницы, а скрипты на стороне клиента не могли получить доступ к данным пользователя.

Я создал контроллеры с пространством имен (Api::V1::) для пользователей, карт животных и сеансов. В моем контроллере приложений я создал два вспомогательных метода — «logged_in?» и «current_user», которые я включил в свой контроллер сеансов для процесса аутентификации. Кроме того, в действии создания контроллера сеансов я использовал метод аутентификации, предоставляемый has_secure_password (включенный в модель пользователя), чтобы убедиться, что пользователь, найденный в базе данных по имени пользователя, предоставляет правильный пароль. Я использовал гем BCrypt и password_digest для хеширования паролей пользователей.

В контроллере сеансов я также определил два дополнительных действия — «logged_in» и «logout». Действие «logged_in» использует вспомогательный метод «logged_in?», определенный в Application Controller, для вывода JSON во внешний интерфейс, который включает информацию «current_user» и статус «logged_in: true» — если пользователь на самом деле вошел в систему. В противном случае действие отображает JSON, который включает пустой объект для пользователя и состояние false для logged_in. Действие «выхода из системы» просто вызывает reset_session и отображает JSON, который включает пустой пользовательский объект и статус «logged_in: false».

Действия пользовательского контроллера вступают в игру для процесса регистрации. Этот контроллер содержит два действия: «создать» и «показать», поскольку это единственные два действия, которые мне требуются для функциональности внешнего интерфейса моего приложения. Действие «создать» (сюрприз, сюрприз) создает нового пользователя и сохраняет его в базе данных при условии, что объект, отправленный с фронтенда, содержит требуемые параметры. Затем метод отображает некоторый JSON обратно во внешний интерфейс после успешного создания пользователя, который включает вновь созданный объект пользователя и статус «logged_in: true». Кроме того, при успешном сохранении пользователя в базе данных идентификатор сеанса устанавливается равным идентификатору нового пользователя. Действие show используется для отображения информации о пользователе через JSON во внешнем интерфейсе.

Для контроллера Animal_Cards я определил действия создания, индексации и уничтожения. Действие создания создает новую карту AnimalCard и сохраняет ее в базе данных, если объект, отправленный из внешнего интерфейса, содержит требуемые параметры. Затем действие отображает объект JSON, включая недавно сохраненную карту animal_card. Действие index отображает все карты животных в формате JSON. Наконец, действие уничтожения находит карточку животного по идентификатору (отправленному из внешнего интерфейса), а затем удаляет эту карточку животного из базы данных.

Внешний дизайн

Что касается внешнего интерфейса, я создал свой внешний интерфейс ReactJS с помощью генератора «create-react-app». Я начал с настройки хранилища в redux/index.js, импорта createStore, combReducers, compose и applyMiddleware из библиотеки redux. Я также импортировал промежуточное ПО thunk из redux-thunk, что позволило мне возвращать функции от создателей моих действий. Я создал файлы authReducer.js и authActions.js, импортировал authReducer в свой файл redux/index.js и использовал его для создания хранилища. В моем файле src/index.js я визуализировал свой компонент приложения, который я подключил к хранилищу и использовал состояние аутентификации хранилища, чтобы определить, вошел ли пользователь в систему. В компоненте приложения я использовал реактивный маршрутизатор для динамического отображения ссылок, которые должны отображаться на панели навигации в зависимости от того, вошел ли пользователь в систему или нет.

В моем недавно созданном файле authAction я создал действие, назначенное переменной с именем «signup». Это действие приняло «пользователь» и «история» в качестве аргументов и вызвало запрос на выборку POST для внутреннего пути пользователей, включая пользователя в виде строкового JSON внутри тело запроса. Как только возвращенное обещание было разрешено, возвращенный JSON был отправлен редюсеру с соответствующим случаем «AUTH_SUCCESS». При достижении редуктора состояние аутентификации было обновлено, чтобы включить возвращенный объект пользователя и статус входа в систему. Также в этом файле authAction были действия для 'login', 'checkLoggedIn' и 'logout' — все они содержали запросы на выборку к серверной части и возвращали промисы, которые были разрешены и конвертировали ответ в JSON, а затем вызывали отправку с типами, соответствующими случаи в authReducer.

Я создал компоненты входа и регистрации с отслеживанием состояния - значениями ключей по умолчанию для указанного состояния являются пустые строки. В обоих компонентах я визуализировал формы — с прослушивателем событий onSubmit, прикрепленным к каждой форме, и прослушивателями onChange, прикрепленными к каждому полю ввода. Прослушиватели событий onChange обновляют значение состояния компонента, которое соответствует измененному входному значению.

Чтобы включить вымирающие виды, я начал с создания файлов animalActions.js и animalReducer.js, импорта animalReducer в redux/index.js и добавления его в хранилище. Я добавил создатель действия fetchAllAnimals в animalActions.js, который отправляет действие с типом «LOADING_ANIMALS» перед вызовом запроса на выборку в ECOS API. Как только возвращенное обещание разрешено, ответ отправляется в виде полезной нагрузки с типом «FETCH_ALL_ANIMALS». Это соответствует случаю в AnimalReducer и обновляет состояние, чтобы включить всех животных, которые были возвращены из запроса на выборку. Я сопоставил это действие отправки с реквизитами в компоненте SearchPage, который вызывает действие внутри хука componentDidMount, а также отображает компонент SearchBar с видами из состояния хранилища, переданными ему в качестве реквизитов.

SearchBar имеет состояние компонента с запросом (по умолчанию пустая строка), данными (по умолчанию пустой массив) и ключами filteredData (по умолчанию пустой массив). Компонент SearchBar использует хук ComponentDidMount, чтобы установить ключ данных состояния компонента для видов, которые были переданы в качестве реквизита из SearchPage. Компонент отображает элемент ввода, который представляет панель поиска и компонент SpeciesSearchResults, которому в качестве реквизита передается состояние filteredData компонента SearchBar. К входному элементу подключен прослушиватель событий onChange, который вызывает метод, использующий setState для обновления запроса и ключей filteredData. Чтобы отфильтровать данные, метод filter вызывается для ранее установленного значения данных состояния, возвращая элементы данных, которые имеют атрибут имени, соответствующий значению запроса. Затем filteredData повторяется, и каждый отдельный объект визуализируется как компонент Animal.

Каждый визуализируемый компонент Animal включает кнопку, которую пользователь может щелкнуть, чтобы сохранить вид на своей панели инструментов. Эта кнопка имеет прослушиватель событий onClick, который вызывает действие отправки saveAnimal. Это действие отправляет запрос POST на серверный API, чтобы сохранить вид в базе данных и связать его с текущим пользователем. Когда пользователь переходит к своей информационной панели, компонент информационной панели вызывает действие отправки fetchSavedAnimals в ComponentDidMount, чтобы получить доступ к сохраненным видам из серверной части. Компонент отображает компонент AnimalCard для каждого из сохраненных пользователем видов, каждый из которых имеет кнопку для удаления вида с панели инструментов, которая вызывает действие отправки, которое отправляет запрос на выборку DELETE на серверную часть для удаления вида из базы данных.

Стиль

Для стиля моего приложения я включил TailwindCSS в свой интерфейс, который после прочтения документации было очень легко и приятно реализовать! С помощью этого фреймворка я смог придать моему приложению отличный стиль!