portaldacalheta.pt
  • Основен
  • Kpi И Анализ
  • Инвеститори И Финансиране
  • Други
  • Финансови Процеси
Back-End

Изградете елегантни компоненти на релси с обикновени стари рубинени предмети



Вашият уебсайт се движи напред и вие бързо се разраствате. Ruby / Rails е най-добрият ви вариант за програмиране. Вашият екип е по-голям и вече сте оставили концепцията 'дебели модели, тънки драйвери' ( дебели модели, кльощави контролери ) като стил на дизайн за вашите Rails приложения. Все пак не искате да спрете да използвате Rails.

Няма проблем. Днес ще обсъдим как да използвате най-добрите практики за OOP, за да направите кода си по-чист, по-изолиран и отделен.



Заслужава ли си да рефакторирате вашето приложение?

Нека започнем, като разгледаме как трябва да решите дали вашето приложение е добър кандидат за рефакторинг.



Ето списък с показатели и въпроси, които обикновено си задавам, за да определя дали моите кодове се нуждаят от рефакторинг или не.

  • Бавни тестови единици. Тестовите единици PORO обикновено работят бързо, с добре изолирани кодове, така че бавно работещите тестове често могат да бъдат индикатор за лош дизайн или свръхсвързани отговорности.
  • FAT модели или контролери. Модел или контролер с повече от 200 редове код ( МЯСТО ) обикновено е добър кандидат за рефакторинг.
  • Прекалено дълга кодова база. Ако имате ERB / ​​HTML / HAML с повече от 30 000 МЯСТО или изходен код на Ruby (без Скъпоценни камъни ) с повече от 50 000 МЯСТО , има голям шанс, че ще трябва да рефакторирате.

Опитайте да използвате нещо подобно, за да разберете колко реда на изходния код на Ruby имате:



find app -iname '*.rb' -type f -exec cat {} ;| wc -l

как да събираме данни от Twitter

Тази команда ще търси всички файлове с разширение .rb (рубинни файлове) в папката / app, след което ще отпечата броя на редовете. Моля, имайте предвид, че това число е само приблизително, тъй като в него ще бъдат включени редове за коментари.



Друг по-точен и информативен вариант е да използвате задачата гребло stats Rails, който излага кратко обобщение на редове код, брой класове, брой методи, съотношението на методите към класовете и съотношението на редове код на метод:

*bundle exec rake stats* +----------------------+-------+-----+-------+---------+-----+-------+ | Nombre | Líneas | LOC | Clase | Método | M/C | LOC/M | +----------------------+-------+-----+-------+---------+-----+-------+ | Controladores | 195 | 153 | 6 | 18 | 3 | 6 | | Helpers | 14 | 13 | 0 | 2 | 0 | 4 | | Modelos | 120 | 84 | 5 | 12 | 2 | 5 | | Mailers | 0 | 0 | 0 | 0 | 0 | 0 | | Javascripts | 45 | 12 | 0 | 3 | 0 | 2 | | Bibliotecas | 0 | 0 | 0 | 0 | 0 | 0 | | Controlador specs | 106 | 75 | 0 | 0 | 0 | 0 | | Helper specs | 15 | 4 | 0 | 0 | 0 | 0 | | Modelo specs | 238 | 182 | 0 | 0 | 0 | 0 | | Petición specs | 699 | 489 | 0 | 14 | 0 | 32 | | Routing specs | 35 | 26 | 0 | 0 | 0 | 0 | | Vista specs | 5 | 4 | 0 | 0 | 0 | 0 | +----------------------+-------+-----+-------+---------+-----+-------+ | Total | 1472 |1042 | 11 | 49 | 4 | 19 | +----------------------+-------+-----+-------+---------+-----+-------+ Código LOC: 262 Prueba LOC: 780 Ratio Código a Prueba: 1:3.0
  • Мога ли да извличам повтарящи се модели в моята кодова база?

Разделяне в действие

Нека започнем с пример от реалния живот.



Да предположим, че искаме да напишем приложение, което проследява времето за хора, които джогинг; На главната страница потребителят може да види въведените часове.



Всеки от записите за време има дата, разстояние, продължителност и допълнителна подходяща информация за състоянието (напр. Времето, тип терен и т.н.) и средна скорост, която може да бъде изчислена, когато е необходимо.

Нуждаем се от отчет, който да показва средната скорост и разстояние за седмица. Ако средната скорост на входа е по-голяма от средната скорост общо, ние ще уведомим потребителя чрез SMS (за този пример ще използваме Nexmo RESTful API за да изпратите SMS).



Главната страница ще ви позволи да изберете разстоянието, датата и часа, в които бягате, за да създадете запис, подобен на този:

Имаме и страница estadísticas, която по същество е седмичен отчет, който включва средната скорост и изминатото разстояние за седмица.

  • Можете да видите пробата онлайн тук .

Кодът

Структурата на директориите на aplicación изглежда подобно на това:

⇒ tree . ├── assets │ └── ... ├── controllers │ ├── application_controller.rb │ ├── entries_controller.rb │ └── statistics_controller.rb ├── helpers │ ├── application_helper.rb │ ├── entries_helper.rb │ └── statistics_helper.rb ├── mailers ├── models │ ├── entry.rb │ └── user.rb └── views ├── devise │ └── ... ├── entries │ ├── _entry.html.erb │ ├── _form.html.erb │ └── index.html.erb ├── layouts │ └── application.html.erb └── statistics └── index.html.erb

Няма да обсъждам модела Usuario тъй като не е нещо необичайно, тъй като го използваме с Девиз за внедряване на удостоверяване.

Що се отнася до модела Entrada, той съдържа бизнес логиката за нашето приложение.

Всеки Entrada принадлежи на Usuario.

Ние проверяваме наличието на следните атрибути за всеки вход distancia, períodode_tiempo, fecha_hora и estatus.

Всеки път, когато създаваме запис, сравняваме средната скорост на потребителя със средната стойност на всички потребители в системата и уведомяваме потребителя чрез SMS, използвайки Nexmo (Няма да обсъждаме как се използва библиотеката Nexmo, въпреки че исках да демонстрирам случай, в който използваме външна библиотека).

  • Gist проба

Имайте предвид, че Entrada моделът съдържа повече от бизнес логика сама. Той също така обработва някои проверки и повиквания.

entries_controller.rb има дяловете ЖЕСТКО main (макар и без актуализация). EntriesController#index получава записите за текущия потребител и сортира записите по дата на създаване, докато EntriesController#create създайте нов запис. Не е необходимо да се обсъждат очевидните или отговорностите на EntriesController#destroy :

  • Gist проба

Докато statistics_controller.rb отговаря за изчисляването на седмичния отчет, StatisticsController#index получава входовете за влезлия потребител и ги групира по седмици, използвайки #group_by който е в класа Изброими в Rails. След това опитайте да декорирате резултатите, като използвате някои частни методи.

  • Gist проба

Тук не обсъждаме мнения много, тъй като изходният код е обяснителен.

По-долу е изгледът за изброяване на записите за влезлия потребител (index.html.erb). Това е моделът, който ще се използва за показване на резултатите от действието на индекса (метода) във входния манипулатор:

  • Gist проба

Имайте предвид, че използваме render @entries Частици, за да приведе споделения код в частичен модел _entry.html.erb за да можем да запазим нашия код СУХА и за многократна употреба:

  • Gist проба

Същото се отнася и за _forma частично. Вместо да използваме същия код с (нови и редактирани) действия, ние създаваме многократна частична форма:

  • Gist проба

По отношение на изгледа на страницата на седмичния отчет, statistics/index.html.erb показва някои статистически данни и отчети за седмичните дейности на потребителя чрез групиране на някои записи:

  • Gist проба

И накрая, помощник за входове, entries_helper.rb, включва две помощници readable_time_period и readable_speed което трябва да направи атрибутите по-лесни за четене:

  • Gist проба

Засега нищо твърде сложно.

Повечето от вас могат да твърдят, че рефакторирането на това противоречи на принципа ЦЕЛУВАЙ и ще направи системата по-сложна.

Така че наистина ли това приложение трябва да бъде рефакторирано?

Абсолютно не , но ние ще го разгледаме само за примерни цели.

В крайна сметка, ако разгледате следващия раздел и характеристиките, които показват, че дадено приложение се нуждае от рефакторинг, става очевидно, че приложението в нашия пример не е валиден кандидат за рефакторинг.

Кръговрата на живота

Нека започнем с обяснение на структурния модел MVC в Rails.

Обикновено започва с търсачката, като отправя заявка като https://www.toptal.com/jogging/show/1.

Уеб сървърът получава заявката и използва rutas за да се определи какво controlador използване.

функционалните документи са предназначени за

Контролерите извършват работата по анализ на потребителски заявки, доставки на данни, бисквитки, сесии и т.н., а след това питат modelo вземете данните.

modelos Те са класове на Ruby, които говорят с базата данни, запазват и валидират данните, изпълняват бизнес логиката и правят тежката работа. Изгледите са това, което потребителят може да види: HTML, CSS, XML, Javascript, JSON.

Ако искаме да покажем последователността на заявка за жизнения цикъл на Rails, тя ще изглежда така:

Релси, разединяващи жизнения цикъл на MVC

Това, което искам да постигна, е да добавя повече абстракция, използвайки PORO и да направя модела нещо подобно на следното за действията create/update

Диаграмата на релсите създава форма

И нещо подобно на това за действия list/show :

Запитване за списък с диаграми на релси

Добавяйки абстракции от PORO, ние гарантираме пълно разделение между отговорностите СЪКЪЛ , нещо, което Rails не владее напълно.

Насоки

За да постигна новия дизайн, ще използвам указанията по-долу, но имайте предвид, че това не са правила, които трябва да спазвате до писмото. Мислете за тях като за гъвкави насоки, които улесняват рефакторинга.

  • Моделите ActiveRecord могат да съдържат асоциации и константи, но нищо друго. Това означава, че няма да има обаждания (използвайте сервизни обекти и добавете повикванията там) и няма проверки (употреба Формирайте обекти да включва имена и валидации за модела).

  • Дръжте контролерите като тънки слоеве и винаги извиквайте сервизни обекти. Някои от вас може да се чудят защо да използват контролери, ако искаме да продължим да извикваме сервизни обекти, за да съдържат логиката? Е, контролерите са добро място да имате маршрутизиране HTTP, разбор на параметри, удостоверяване, договаряне на съдържание, извикване на правилната услуга или обект на редактор, улавяне на изключения, форматиране на отговори и връщане на правилното състояние на HTTP кода.

  • Услугите трябва да извикват обекти Заявка и не съхраняват състояние. Използвайте некласови методи на екземпляр. Трябва да има много малко публични методи, записани с СЪКЪЛ .
  • Заявки трябва да се направи с предмети заявка . Обектни методи Заявка трябва да върне обект, a хеш или масив , но не и ActiveRecord асоциация.

  • Избягвайте да използвате Помощници , по-добре използвайте декоратори. Защо? Често срещана трудност с помощници в Rails е, че те могат да бъдат превърнати в куп не- ОО , които споделят име на интервал и се припокриват помежду си. Но е много по-лошо, фактът, че не може да се използва полиморфизъм с помощници Rails - чрез предоставяне на различни изпълнения за различен контекст или тип и заобикаляне или подкласифициране на помощници. Мисля, че видовете помощник в Rails те обикновено трябва да се използват за полезни методи, а не за конкретни случаи на употреба; как да форматирате атрибути на модел за всяка логика на презентацията. Поддържайте ги леки и лесни за следване. По-добре декоратори / Делегати. ** Защо? В края на краищата, грижите изглежда са част от Rails и те могат да изсъхнат ( Изсушавам ) код, когато се споделя между множество модели. По-големият проблем обаче е, че опасенията не правят обекта на модела по-сплотен. Само кодът е по-добре организиран. С други думи, няма реална промяна в API на модела.

  • Опитайте се да извлечете Ценни предмети от моделите за да запазите кода си по-чист и групираните атрибути.

  • Винаги предавайте променлива на екземпляр за всеки изглед.

Refactorizar

Преди да започна, искам да обсъдя нещо друго. Когато рефакторингът започне, обикновено се чудите: „Добър рефакторинг ли е?“

Ако смятате, че правите повече разделение или изолация между отговорностите (дори това да означава добавяне на повече код и нови файлове), това е нещо добро. В крайна сметка отделянето на приложение е много добра практика и ни улеснява да направим подходящ единичен тест.

Няма да обсъждам неща, като например преместване на логика от контролери към модели, тъй като предполагам, че правите това и ви е удобно да използвате Rails (обикновено Skinny Controller и FAT модел).

За да запазя тази статия кратка, няма да обсъждам тестване, но това не означава, че не трябва да тествате.

Напротив, трябва винаги започвайте с тест за да сте сигурни, че нещата вървят добре, преди да продължите напред. Това е нещо задължително, особено когато правите рефакторинг.

След това можем да внедрим промени и да се уверим, че тестовете преминават през съответните части на кода.

Извличане на ценности

Първо, какво е обект на стойност?

Мартин Фаулър Обяснете:

Обектът на стойност е малък обект, например обект на пари или период от време. Тяхната ключова характеристика е, че те следват стойностната семантика, а не референтната семантика.

Понякога можете да попаднете в ситуация, в която една концепция заслужава своя абстракция и когато нейното равенство не се основава на ценности, а на идентичност. Примери за това могат да бъдат: Дата, URI и име на път на Ruby. Извличането на ценен обект (или модел на домейн) е голямо удобство.

Защо да се занимавам?

Едно от големите предимства на стойностния обект е, че те помагат да се получи изразителност във вашия код. Вашият код ще бъде по-ясен или поне може да бъде, ако имате добри практики за именуване. Тъй като Value Object е абстракция, това води до по-ясни кодове и по-малко грешки.

Друга печалба е неизменност . Неизменността на обектите е много важна. Когато съхраняваме определени набори от данни, които могат да се използват в обект на стойност, обикновено не харесвам данните, които се манипулират.

Кога е полезно това?

Няма идеален отговор на този въпрос. Правете това, което е във ваш интерес и което има най-голямо значение в дадена ситуация.

Освен това има някои насоки, които използвам, за да ми помогне да взема това решение.

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

Как се прави това?

Ценностите трябва да следват определени правила:

  • Ценностите трябва да имат множество атрибути.
  • Атрибутите трябва да бъдат неизменни през целия жизнен цикъл на обекта.
  • Равенството се определя от атрибутите на обекта.

В нашия пример ще създам обект на стойност EntryStatus, за да абстрахирам атрибутите Entry#status_weather и Entry#status_landform към собствен клас, който изглежда така:

  • Gist проба

Забележка: Това е само PORO (Plain Old Ruby Object), той не наследява от ActiveRecord::Base Дефинирали сме методи за четене на нашите атрибути и ги присвояваме при стартиране. Също така използваме сравним микс, за да съпоставим обекти, използвайки метода ().

Можем да модифицираме модела Entry за да използваме обекта на стойност, който сме създали:

  • Gist проба

Също така можем да модифицираме метода EntryController#create за да използвате съответно новия обект на стойност:

  • Gist проба

Извличане на обекти на услугата

Какво е обект на услуга?

Задачата на обект на услуга е да държи кода по време на определено пространство на бизнес логиката. За разлика от стила 'Дебел модел' , където малък брой обекти съдържат много, много методи, за цялата необходима логика, използването на сервизни обекти води до много класове, всеки от които служи за уникална цел.

Защо? Какви са ползите?

  • Разкъсвам. Сервизните обекти ви помагат да постигнете по-голяма изолация между обектите.

  • Видимост. Сервизните обекти (ако са правилно именувани) показват какво прави приложението. Мога да разгледам директорията на услугите, за да видя какви възможности предоставя едно приложение.

    c корпорациите предлагат по-голяма правна защита на собствениците от корпорациите.
  • Изчистени модели и драйвери. Контролерите се включват при поискване ( параметри , session, cookies) в аргументите, той ги предава на услугата и ги пренасочва или оставя в зависимост от отговора на услугата. Докато моделите се занимават само с асоциации и постоянство. Извличането на код от контролери / модели към сервизни обекти би поддържало СЪКЪЛ и това допълнително ще отдели кода. Отговорността на модела ще трябва да се справя само с асоциации и да запазва / изтрива записи, докато обектът на услугата ще има само една отговорност ( СЪКЪЛ ). Това води до по-добър дизайн и по-добри тестови единици.
  • СУХА и Приемете промяната. Поддържам услугите възможно най-прости и малки. Съставям сервизни обекти с други сервизни обекти и ги използвам повторно.

  • Почистете и ускорете вашия тестов пакет. Услугите са бързи и лесни за тестване, тъй като представляват малки обекти в Ruby с входна точка (наречен метод). Комплексните услуги се състоят от други услуги, така че лесно можете да разделите тестовете си. Също така, използването на сервизни обекти улеснява извличането на свързани обекти, без да се налага да зареждате цялата среда на релсите.

  • Изкупи от всяко място. Сервизните обекти със сигурност ще се извикват от контролерите, както и от сервизни обекти, DelayedJob / Rescue / Sidekiq Jobs, Rake задачи, конзола и т.н.

От друга страна, нищо не е идеално. Един недостатък на обектите на услугата е, че те могат да бъдат преувеличени за всяко малко действие. В тези случаи можете в крайна сметка да усложните и да не опростите кода си.

Кога трябва да извлечете сервизни обекти?

Тук също няма фиксирано правило.

Обикновено сервизните обекти са най-подходящи за средни до големи системи: тези с прилично количество логика, извън стандартните CRUD операции.

Така че, когато смятате, че парче код не принадлежи в директорията, на мястото, където сте щяли да го добавите, е добра идея да го преразгледате и да видите дали би било по-добре, ако отиде в обект на услуга.

Ето някои указатели за това кога да се използват обекти на услугата:

  • Действието е сложно.
  • Действието достига до множество модели.
  • Действието взаимодейства с външна услуга.
  • Действията не са основна грижа на подчертания модел.
  • Има няколко начина за извършване на действието.

Как трябва да проектирате Service Objects?

Проектирането на класа за обект на услуга е сравнително лесно, тъй като нямате нужда скъпоценни камъни не трябва да научавате a DLS ново, но можете, горе-долу, да разчитате на уменията за софтуерен дизайн, които вече притежавате.

Обикновено използвам следните насоки и конвенции, за да проектирам обекта на услугата:

  • Не съхранявайте състоянието на обекта.
  • Използвайте методи на пример, а не методи на клас.
  • Трябва да има много малко публични методи (за предпочитане такъв, който поддържа * СИКЛ .
  • Методите трябва да връщат богати, а не булеви обектни резултати.
  • Услугите отиват в директорията app/services . Съветвам ви да използвате поддиректории за силни домейни на бизнес логика. Например файлът app/services/report/generate_weekly.rb ще определи Report::GenerateWeekly докато app/services/report/publish_monthly.rb ще определи Report::PublishMonthly.
  • Услугите започват с глагол (и не завършват в услуга): ApproveTransaction, SendTestNewsletter, ImportUsersFromCsv.
  • Услугите реагират на извикания метод. Открих, че използването на друг глагол го прави малко излишен: ApproveTransaction.approve () не се чете добре. Също така, нареченият метод е де факто методът на ламбда , procs , и обекти на метод.

Ако погледнете StatisticsController#index, ще забележите група методи (weeks_to_date_from, weeks_to_date_to, avg_distance и т.н.), групирани в контролера. Това не е добре. Помислете за последствията, ако искате да генерирате седмичния отчет извън statistics_controller.

В нашия случай ще създадем Report::GenerateWeekly и извлечете логическия отчет от StatisticsController:

  • Gist проба

Така че StatisticsController#index сега изглежда по-чисто:

  • Gist проба

Прилагайки шаблона на обект на услугата, ние групираме код около сложно и специфично действие и насърчаваме създаването на по-малки и по-ясни методи.

Домашна работа: помислете за използване Ценен обект за WeeklyReport вместо Struct .

Извличане на обекти Заявка Контролери

Какво е обект Заявка ?

Предмет Заявка е PORE, което представлява база данни за заявки. Може да се използва повторно на различни места в приложението, като в същото време скрива логиката на заявката. Той също така осигурява добра изолирана единица за тестване.

Трябва да извлечете сложни SQL / NoSQL заявки в техните собствени класове.

Всеки обект Заявка отговаря за връщането на набор от резултати въз основа на критериите / бизнес правилата.

В този пример нямаме заявка ( заявка ) сложен, така че използвайте обект Заявка не би било ефективно. За целите на демонстрацията обаче ще извлечем заявката в Report::GenerateWeekly#call и ние ще създадем generate_entries_query.rb:

  • Gist проба

И в Report::GenerateWeekly#call, нека заменим:

def call @user.entries.group_by(&:week).map do |week, entries| WeeklyReport.new( ... ) end end

с:

def call weekly_grouped_entries = GroupEntriesQuery.new(@user).call weekly_grouped_entries.map do |week, entries| WeeklyReport.new( ... ) end end

Моделът на обекта заявка (заявка) помага да поддържате логиката на модела си строго свързана с поведението на класа, като същевременно държите вашите контролери слаби. Защото те не са нищо друго освен обикновени стари класове Ruby , обектите заявка не е необходимо да наследяват от ActiveRecord::Base и не трябва да носят отговорност за нищо друго освен за изпълнение на заявката.

Извлечете Създаване на запис в обект на услуга

Сега ще извлечем логиката на създаване на нов запис към нов обект на услуга. Нека използваме конвенцията и да създадем CreateEntry:

  • Gist проба

А сега нашите EntriesController#create е както следва:

def create begin CreateEntry.new(current_user, entry_params).call flash[:notice] = 'Entry was successfully created.' rescue Exception => e flash[:error] = e.message end redirect_to root_path end

Още проверки на обект във форма

Сега нещата започват да стават по-интересни.

Не забравяйте, че в нашите насоки се съгласихме, че искаме моделите да имат асоциации и константи, но нищо друго (без валидиране или обаждания). Затова нека започнем с премахване на допълнителните описания и вместо това да използваме обект Shape.

Обектът Shape е PORO (Plain Old Ruby Object). Поемете командата на обекта на контролера / услугата, когато трябва да говорите с базата данни.

Защо да използвам Shape обекти?

Когато трябва да рефакторирате приложението си, винаги е добре да имате предвид, основната отговорност ( СЪКЪЛ ).

СЪКЪЛ ви помага да вземате по-добри дизайнерски решения по отношение на отговорността, която класът трябва да носи.

Моделът на вашата таблица на базата данни (модел ActiveRecord в контекста на Rails), например, представлява уникален запис на база данни в код, така че няма причина да се занимавате с каквото и да е, което вашият потребител прави.

Тук влиза обектът Shape.

Обектът Shape е отговорен за представянето на фигура във вашето приложение. Така че всяко поле за въвеждане може да се третира като атрибут в класа. Можете да проверите дали тези атрибути отговарят на някои правила за валидиране и можете да предадете 'чистите' данни там, където трябва да отидат (например вашия модел на база данни или може би вашият конструктор за търсене на заявки).

Кога трябва да използвате обект Shape?

  • Когато искате да извлечете валидации от Rails модели.
  • Когато множество модели могат да бъдат актуализирани с един метод за доставка, трябва да създадете обект Shape.

Това ви позволява да поставите цялата логика на формата (конвенции за именуване, проверки и други) на едно място.

Как да създам обект Shape?

  • Създайте прост клас Ruby.
  • Включва ActiveModel::Model (В Rails 3 вместо това трябва да включите Name, Conversion и Validation).
  • Започнете да използвате новия си клас на фигура, сякаш е обикновен модел на ActiveRecord, където най-голямата разлика е, че не можете да продължите с данните, съхранявани в този обект.

Моля, обърнете внимание, че можете да използвате скъпоценен камък реформа , но продължавайки с PORO, ще създадем entry_form.rb което изглежда така:

  • Gist проба

И ние ще модифицираме CreateEntry за да започнете да използвате обекта Format EntryForm:

class CreateEntry ...... ...... def call @entry_form = ::EntryForm.new(@params) if @entry_form.valid? .... else .... end end end

Забележка: Някои от вас ще кажат, че няма нужда от достъп до обекта Shape от обекта Service и че можем да извикаме Shape обекта директно от контролера, което е валиден аргумент. Предпочитам обаче да имам ясен поток, затова винаги извиквам Shape object от Service object.

Преместване на обажданията към обект на услуга.

Както се договорихме преди, не искаме нашите модели да съдържат проверки и обаждания. Извличахме валидациите с помощта на Shape обекти. Но все още използваме някои повиквания (after_create в модел Entry compare_speed_and_notify_user).

Защо искаме да премахнем допълнителните описания от моделите?

Разработчици на релси те обикновено започват да забелязват болка при обажданията, по време на тестовете. Ако не тествате вашите модели ActiveRecord, ще започнете да забелязвате болката по-късно, тъй като приложението ви расте и тъй като е необходима повече логика за извикване или избягване на обаждания.

después_* повикванията се използват предимно във връзка със запазване или продължаване с обекта.

След като обектът бъде запазен, целта на обекта (напр. Отговорност) е изпълнена. Така че, ако все пак видим, че се извикват повиквания, след като обектът е запазен, това вероятно са повиквания, които искат да излязат от зоната на отговорност на обекта и тогава се сблъскваме с проблеми.

В нашия случай изпращаме SMS до потребителя, който не е свързан с входния домейн.

Един прост начин за решаване на проблема е преместването на повикването към свързания обект на услугата. В крайна сметка изпращането на SMS до съответния потребител е свързано с Обекта на услугата CreateEntry а не моделът Entry като такъв.

Правейки това, вече не трябва да изключваме, compare_speed_and_notify_user в нашите тестове. Направихме това лесен въпрос, създавайки запис без необходимост от изпращане на SMS и следваме добър обектно-ориентиран дизайн, като се уверим, че нашите класове носят уникална отговорност ( СЪКЪЛ ).

какво можеш да направиш с ruby ​​on rails

Така че сега CreateEntry това е нещо подобно на това:

  • Gist проба

Използвайте декоратори вместо помощници

Въпреки че можем лесно да използваме колекцията Драпер от оглед на модели и декоратори, оставам с PORO, за тази статия, както правех и досега.

Това, от което се нуждая, е клас, който извиква методи за декорирания обект.

Мога да използвам method_missing за да приложа това, но ще използвам стандартната Ruby библиотека, SimpleDelegator. Следващият код показва как да използвате SimpleDelegator за внедряване на нашия основен декоратор:

% app/decorators/base_decorator.rb require 'delegate' class BaseDecorator

Защо методът _h

Този метод действа като прокси за контекста на изгледа. По подразбиране контекстът на изгледа е екземпляр на клас на изглед, който е ActionView::Base. Можете да получите достъп до помощници на гледните точки, както следва:

_h.content_tag :div, 'my-div', class: 'my-class'

За да бъде по-удобно, добавяме метод decorado a ApplicationHelper:

module ApplicationHelper # ..... def decorate(object, klass = nil) klass ||= '#{object.class}Decorator'.constantize decorator = klass.new(object, self) yield decorator if block_given? decorator end # ..... end

Сега можем да преместим помощници EntriesHelper към декоратори:

# app/decorators/entry_decorator.rb class EntryDecorator

И можем да използваме readable_time_period и readable_speed както следва:

# app/views/entries/_entry.html.erb - + - +

Структура след рефакторинг

В крайна сметка получихме повече файлове, но това не е непременно лошо нещо (и помнете това, от самото начало бяхме наясно, че този пример е за демонстрационни цели и не непременно беше добър случай за рефакторинг):

app ├── assets │ └── ... ├── controllers │ ├── application_controller.rb │ ├── entries_controller.rb │ └── statistics_controller.rb ├── decorators │ ├── base_decorator.rb │ └── entry_decorator.rb ├── forms │ └── entry_form.rb ├── helpers │ └── application_helper.rb ├── mailers ├── models │ ├── entry.rb │ ├── entry_status.rb │ └── user.rb ├── queries │ └── group_entries_query.rb ├── services │ ├── create_entry.rb │ └── report │ └── generate_weekly.rb └── views ├── devise │ └── .. ├── entries │ ├── _entry.html.erb │ ├── _form.html.erb │ └── index.html.erb ├── layouts │ └── application.html.erb └── statistics └── index.html.erb

заключение

Въпреки че се фокусираме върху Rails в тази публикация в блога, RoR (Ruby on Rails) не зависи от обекти на услуги или други PORO. Можете да използвате този подход с всеки рамкова мрежа , мобилно или конзолно приложение.

При използване MVC Като архитектура всичко се слепва и забавя, защото повечето промени оказват влияние върху други части на приложението. Също така ви принуждава да помислите къде да поставите някаква бизнес логика - трябва ли да е в модела, контролера или изгледа?

Използвайки просто PORO, сме преместили бизнес логиката към модели или услуги, които не наследяват от ActiveRecord, което вече е печалба, да не говорим, че имаме по-ясен код, който поддържа СЪКЪЛ и по-бързи модулни тестове.

Изчистената архитектура се опитва да постави полетата за използване в центъра / горната част на вашата структура, така че лесно да можете да видите какво прави вашето приложение. Това също улеснява приемането на промени, тъй като е по-модулно и изолирано. Надявам се да съм показал как да Обикновени стари рубинени предмети и повече абстракции, той разделя опасенията, опростява тестването и помага за създаването на чист, поддържаем код.

Свързани: Какви са предимствата на Ruby on Rails? След две десетилетия програмиране. Използвайте релси

Слушалки: Структури за преодоляване на задънена улица при преговорите

Финансови Процеси

Слушалки: Структури за преодоляване на задънена улица при преговорите
Как успеят цифровите трансформации

Как успеят цифровите трансформации

Бъдещето На Работата

Популярни Публикации
Първи стъпки с модули и модулна разработка отпред
Първи стъпки с модули и модулна разработка отпред
Най-добри практики за уеб оформление: Анализирани са 12 вечни модела на потребителския интерфейс
Най-добри практики за уеб оформление: Анализирани са 12 вечни модела на потребителския интерфейс
H-1B: Пътешествие на разработчика на iOS от Хондурас до Силициевата долина
H-1B: Пътешествие на разработчика на iOS от Хондурас до Силициевата долина
Agile UX: Как да включите UX и продуктовия дизайн в Agile
Agile UX: Как да включите UX и продуктовия дизайн в Agile
Съвети за привличане, управление и задържане на разработчици на софтуер
Съвети за привличане, управление и задържане на разработчици на софтуер
 
Въведение в търговията с дълбоко обучение в хедж фондове
Въведение в търговията с дълбоко обучение в хедж фондове
Magento 2: Ревизия или революция?
Magento 2: Ревизия или революция?
Кутия с инструменти на Forecaster’s: Как да извършите симулации на Монте Карло
Кутия с инструменти на Forecaster’s: Как да извършите симулации на Монте Карло
Ръководител на растежа
Ръководител на растежа
Вицепрезидент по комуникациите
Вицепрезидент по комуникациите
Популярни Публикации
  • примери за страхотен ux дизайн
  • какво дружество съм аз
  • полезен начин за визуално представяне на данните
  • какво е услуга за запознанства
  • генератор на статичен сайт на node js
  • как да програмирам робот в java
Категории
  • Kpi И Анализ
  • Инвеститори И Финансиране
  • Други
  • Финансови Процеси
  • © 2022 | Всички Права Запазени

    portaldacalheta.pt