В цій статті я поділюся декількома принципами, які допомагають мені в роботі. Їх не завжди використовують початківці і ті, хто недавно змінив технологію розробки. В кінці кожного пункту я додам ресурси, які були корисними для мене.
Тестування
Розпочнемо з тестування. Для впевненості у тому, що сайт працює правильно, потрібно його тестувати. Rails по замовчуванню використовує Minitest у ролі фреймворку для тестування, але я віддаю перевагу RSpec.
Test-driven development або TDD — процес розробки застосунку, який передбачає створення тестів перед додаванням нового функціоналу. З першого погляду це може здаватися як марна трата часу — потрібно витратити додатковий час для написання тесту, вирішити що протестувати. Але вони спрощують процес додавання нового і редагування старого функціоналу. Вам не потрібно після кожної зміни перевіряти в браузері чи код працює, достатньо запустити тести.
Нижче декілька ресурсів з навчальним матеріалом про TDD:
- How to setup a Rails app for Test-Driven- and Behavior-Driven Development with Rspec and Capybara
- Learn the First Best Practices for Rails and RSpec — SitePoint
- What Test Driven Development is and Why You Should Care
- End-to-End Testing with RSpec Integration Tests and Capybara
DRY(Don't repeat yourself)
Rails підтримує ідею DRY (не повторюй сам себе). Принцип полягає в тому, щоб при потребі використовувати один і той же код в декількох місцях. Це призводить до зменшення кількість помилок та пришвидшує розробку.
Правила Сенді Метц
Сенді Метц добре відомий Рубі інженер. Вона є автором таких книг як: Practical Object-Oriented Design in Ruby і Ruby and 99 Bottles of OOP. Нижче наведені рекомендовані нею правила:
1. Клас не повинен бути довшим за сто рядків.
Хорошим способом дотримання цього правила є винесення певного функціонала в інші модулі (include/extend/prepend). Нижче наведені посилання на корисні ресурси.
2. Методи не повинні бути довшими за п'ять лінійок.
Чим менша кількість лінійок, тим простіший метод. Назва методу повинна чітко описувати те що він виконує. Правило п'яти лінійок пов'язане з if-else оператором. Саме його реалізація потребує стільки ж рядків. На практиці важко описати метод п'ятьма лінійками, старайтесь використати найменше.
3. Передавайте не більше чотирьох аргументів в метод.
Багато параметрів в результаті дадуть:
- Складність сприйняття
- Важче тестувати, через створення великої кількості даних.
- Чим менше параметрів, тим простіший і зрозуміліший метод.
4. В методі контролера не використовувати більше ніж одну змінну (instance variable).
З одною змінною ви зберігаєте кращий контроль над нею, і запобігаєте її перезаписанню. Шаблон виглядатиме більш чистим та зрозумілим.
Трохи більше про правила можете дізнатися тут: Sandi Metz' Rules For Developers.
Extract method
Важливо, щоб назва методу відповідала його призначенню. Існує хороша практика на кожну дію, створювати окремий метод. Це дозволить швидше зрозуміти логіку новим розробникам, полегшить тестування, та зробить процес визначення незрозумілої поведінки простішим.
SOLID
SOLID – абревіатура принципів проектування класів в програмуванні, розшифровується як:
- Принцип єдиної відповідальності (Single responsibility).
- Принцип відкритості / закритості (Open-closed).
- Принцип підставлення Лісков (Liskov substitution).
- Принцип поділу інтерфейсу (Interface segregation).
- Принцип інверсії залежностей (Dependency invertion).
Дотримання цих правил дозволить вам писати зрозумілий, гнучкий код.
- SOLID Principles in Ruby | Subvisual Blog
- Back to Basics: SOLID
- Ruby Blog - Blog about Ruby and Ruby On Rails (This blog explains each principle especially well.)
N+1 Query
N+1 – проблема шаблону проектування доступу до реляційних баз даних (ActiveRecord), яка впливає на швидкодію запитів та збільшує їхню кількість. Вона з'являється коли потрібно отримати дані з декількох пов'язаних таблиць. Наприклад:
class User
has_many :posts
end
class Post
belongs_to :user
end
posts = Post.last(10)
posts.map { |post| post.user.name }
Вище наведений приклад виконає 11 запитів в БД. Один запит витягне десять останніх Post
записів, і ще десять інших ім'я user
. В Rails є простий і ефективний спосіб вирішення цієї проблеми:
posts = Post.includes(:user).last(10)
posts.map { |post| post.user.name }
Після додавання includes(:user)
, ми зменшимо кількість запитів до двох. Назва цього підходу eager loading. Більше інформації зможете знайти в цих статтях:
- Making sense of ActiveRecord joins, includes, preload, and eager_load
- Faster Rails: Eliminating N+1 queries
- Remove N+1 queries in your Ruby on Rails app
- 10 Tips for Eager Loading to Avoid n+1 Queries in Rails
Memoization
Memoization – процес кешування результатів обчислень методів. Кожний наступний виклик такого методу, буде повертати закешований результат, це збереже ресурси системи та суттєво пришвидшить час відповіді. Memoization доречно використовувати коли метод викликається більше одного разу. Скажімо, вам потрібно створити звіт для компаній у вигляді таблиці з їх назвами в <header>
та деякими обрахунками в <body>
.
class CompaniesController
def index
@companies ||= Company.all
end
private
helper_method :grouped_by_day_activities
helper_method :activities_count
def grouped_by_day_activities
@grouped_by_day ||= Activity.all.group_by(&:day)
end
def activities_count
@activities_count ||= grouped_by_day_activities.map do |day, activities|
grouped_by_company_activities_count = activities.group_by(&:company_id).map do |c_id, activities|
[c_id, activities.count]
end.to_h
[day, grouped_by_company_activities_count]
end.to_h
end
end
# activities_count =>
# { '1/1/2018': { 1: 10, 2: 6, 3: 9 },
# '2/1/2018': { 1: 5, 2: 4, 3: 7} }
.....
/companies/index.haml
%table
%thead
%th Date
- @companies.each do
%th= copmany.name
%tbody
- grouped_by_day_activities.each do |day, activities|
%tr
%td= day
- @companies.each do |company|
%td= activities_count.fetch(day).fetch(company.id, 0)
В цьому випадку кожен метод контролера використовує Memoization. Memoizetion ініціалізується за допомогою ||= (або-рівне) знаку. Почнемо з @companies
змінної. Вона виконується двічі: в <header>
таблиці та <body>
. Під час другого виконання змінної виконався закешований результат, та зменшив кількість запитів в БД.
Наступний метод grouped_by_day_activities
, повертає згруповані за днем дії компаній. Перший раз він викликається в тілі таблиці, а другий з методу activities_count
. Activities_count
повертає хеш згрупованих за day
та company_id дій(activities)
всіх компаній. Цей метод викликається в кожній секції таблиці, тому доречно використовувати memoizetion. Також можна використовувати memoization в більш складних випадках у поєднанні з метапрограмуванням.
- 4 Simple Memoization Patterns in Ruby (and one gem)
- The Basics of Ruby Memoization
- Memoization in Ruby
- The Rubyist's Guide to Memoization
- Memoization in Ruby Using Metaprogramming
Authentication
Аутентифікація – механізм перевірки ідентичності та прав доступу, дозволяє ховати частини програми від користувачів без повноважень. Я рекомендую використовувати devise гем.
Authorization
Авторизація – процес перевірки чи користувач може виконати дію. Уявімо, що система має користувачів трьох типів: адмін, гість, менеджер. Наша ціль дозволити редагувати пости тільки адмінам і менеджерам, в результаті отримаємо:
If user.role == 'admin' || user.role == 'manager'
= link_to 'Edit post', edit_post_path(post)
Припустимо, що по всьому сайті в нас є схожі перевірки. Раптом, ми вирішуємо дозволити редагувати гостям їхні пости. Після цього потрібно пройтися по всіх файлах і змінити умову на таку:
If user.role == 'admin' || user.role == 'manager' || user.role == 'guest'
Використовуючи гем pundit, в проект буде додана директорія /policies, в якій буде зберігатися логіка прав доступу кожної сутності проекту. Наприклад:
class UserPolicy
def can_edit_post?
user.role == 'admin' || user.role == 'manager' || user.role == 'guest'
end
end
If оператор буде виглядати як:
If policy(user).can_edit_post?
= link_to 'Edit post', edit_post_path(post)
Наступного разу коли доведеться змінювати логіку, достатньо оновити код в одному місці. Такий підхід пришвидшує час розробки й зменшує кількість помилок з правами доступу.
Детальніше можна прочитати тут:
7 патернів для рефакторингу Rails застосунку
Рефакторинг – процес оновлення програмного коду, спрямований на підвищення швидкодії, покращення архітектурних рішень, які призведуть до спрощення процесу додавання нового функціоналу, не змінюючи його зовнішньої поведінки. Статті наведені нижче містять загальновідомі способи рефакторингу Rails застосунків.
- 7 Design Patterns to Refactor MVC Components in Rails – SitePoint
- 7 Patterns to Refactor Fat ActiveRecord Models
Background jobs
Швидкодія є одним з найважливіших факторів сучасних веб застосунків. Отримуючи повідомлення від зовнішнього світу, чим швидше користувач отримає результат дії, тим краще. У випадку коли дія повинна виконати щось велике та трудомістке, фонові обчислення будуть правильним рішенням. Фонові чи асинхронні обчислення, виконуються поза основним робочим процесом та не створюють ефекту затримки. Одним з найпопулярніших гемів для фонових робіт є sidekiq.
Відеоуроки
Цікавий та корисний скрінкаст: ruby tapas. Там можна знайти багато відеоуроків різної складності по Рубі.
Висновок
Я вирішив написати цю статтю, тому що лише відносно недавно почав свій шлях rails програміста. Я знаю, як важливо користуватись якісними джерелами, бо досі пам'ятаю всю корисну інформацію, яка, свого часу, допомогла мені справитись з усіма поставленими задачами.
Також дуже важливою є підтримка і спрямування в правильне русло від досвідчених у своїй галузі людей.
Коли починаєш вчити щось нове, найважливіше не здаватися. Особисто мені процес вивчення давався дуже складно. Кожного ранку я прокидався з думкою, «Я нічого не знаю, і не буду знати, я безнадійний в цьому». Інколи я витрачав весь день, щоб виконати п'ятихвилинне завдання. Але я не здавався, після декількох місяців почав отримувати задоволення від того що роблю та краще розуміти як працює rails.
Отже, новачкам можу порекомендувати набратись міцного терпіння, не втрачати натхнення і вірити в себе, адже це основні якості для досягнення успіху в будь-якій сфері.
happy coding ;)
Ще немає коментарів