Build Dumb, Refactor Smart: Ruby on Rails Kodundan Sorunlara Nasıl Masaj Yapılır?
Yayınlanan: 2022-03-11Bazen müşteriler bize gerçekten sevmediğimiz özellik istekleri verir. Müşterilerimizi sevmediğimizden değil, müşterilerimizi seviyoruz. Bu özelliği beğenmediğimizden değil—müşteri tarafından talep edilen çoğu özellik, iş hedefleri ve gelirleriyle mükemmel bir şekilde uyumludur.
Bazen bir özellik isteğini sevmeyiz çünkü onu çözmenin en kolay yolu kötü kod yazmaktır ve aklımıza şık bir çözüm gelmiyor. Bu, çoğumuz Rails geliştiricilerini RubyToolbox, GitHub, geliştirici blogları ve StackOverflow aracılığıyla sonuçsuz aramalara göndererek kendimizi daha iyi hissetmemizi sağlayacak bir mücevher veya eklenti veya örnek kod arıyor.
Refactor Ruby on Rails Kodu
Pekala, size şunu söylemek için buradayım: Kötü kod yazmakta sorun yok. Bazen, kötü Rails kodunun güzel koda yeniden düzenlenmesi, zaman sıkıntısı altında uygulanan kötü düşünülmüş bir çözümden daha kolaydır.
Bu, korkunç yara bandı çözümlerimdeki sorunları çözerken izlemeyi sevdiğim Rails yeniden düzenleme sürecidir:
Alternatif bir bakış açısı için, adım adım yeniden düzenlenen bir özelliğin Git taahhüt günlüğü:
Ve işte Toptal ağındaki bir meslektaşımdan büyük ölçekli yeniden düzenleme hakkında ilginç bir makale daha.
Nasıl yapıldığını görelim.
Görünümler
Adım 1. Görünümlerde Başlayın
Diyelim ki yeni bir özellik için bilet almaya başlıyoruz. Müşteri bize şunu söylüyor: "Ziyaretçiler, karşılama sayfasında aktif Projelerin bir listesini görebilmelidir."
Bu bilet görünür bir değişiklik gerektiriyor, bu nedenle işe başlamak için makul bir yer Görünümler'de olacaktır. Sorun basit ve hepimizin defalarca çözmek için eğitildiği bir sorun. Bunu Yanlış Yolda çözeceğim ve çözümümü uygun alanlara nasıl yeniden yansıtacağımı göstereceğim. Bir Problemi Çözmek Yanlış Yol , doğru çözümü bilmemenin yarattığı sıkıntıdan kurtulmamıza yardımcı olabilir.
Başlamak için, active
adlı bir boole özniteliğine sahip Project
adlı bir modelimiz olduğunu varsayalım. Active'in true'ya eşit olduğu tüm Projects
bir listesini almak istiyoruz, böylece Project.where(active: true)
active
true
each
blokla bunun üzerinde dolaşabiliriz.
app/views/pages/welcome.haml: %ul.projects - Project.where(active: true).each do |project| %li.project= link_to project_path(project), project.name
Ne dediğinizi biliyorum: "Bu asla bir kod incelemesinden geçemez" veya "Müvekkilim kesinlikle bunun için beni kovar." Evet, bu çözüm Model-Görünüm-Denetleyici endişe ayrımını bozar, izlenmesi zor olan başıboş veritabanı çağrılarına neden olabilir ve gelecekte bakımı zor olabilir. Ama bunu yapmanın değerini bir düşünün Yanlış Yol :
- Bu değişikliği sahnelemede 15 dakikadan kısa sürede elde edebilirsiniz.
- İçeride bırakılırsa, bu bloğun önbelleğe alınması kolaydır.
- Bu Rails sorununu düzeltmek basittir (küçük bir geliştiriciye verilebilir).
Adım 2. Kısmi kısımlar
Bunu The Wrong Way yaptıktan sonra kendim hakkında kötü hissediyorum ve kötü kodumu izole etmek istiyorum. Bu değişiklik açıkça yalnızca Görünüm katmanının bir endişesi olsaydı, utancımı kısmi olarak yeniden değerlendirebilirdim.
app/views/pages/welcome.haml: = render :partial => 'shared/projects_list' app/views/shared/projects_list.haml: %ul.projects - Project.where(active: true).each do |project| %li.project= link_to project_path(project), project.name
Bu biraz daha iyi. Açıkçası, hala bir Görünümde Model sorgusu hatası yapıyoruz, ancak en azından bir bakıcı daha sonra geldiğinde ve benim korkunç kısmi durumumu gördüğünde, özellikle bu Rails kodu sorununu çözmenin basit bir yoluna sahip olacaklar. Aptalca bir şeyi düzeltmek, kötü uygulanmış, hatalı bir soyutlamayı düzeltmekten her zaman daha kolaydır.
Adım 3. Yardımcılar
Rails'deki Yardımcılar, Görünümlerinizin bir bölümü için bir DSL (Etki Alanına Özgü Dil) oluşturmanın bir yoludur. Haml veya HTML yerine content_tag'leri kullanarak kodunuzu yeniden yazmanız gerekir, ancak diğer geliştiricilerin 15 satır yazdırılmayan Görünüm kodu için size dik dik bakmalarına gerek kalmadan veri yapılarını değiştirmeye izin verme avantajına sahip olursunuz.
Burada yardımcıları kullanacak olsaydım, muhtemelen li
etiketini yeniden düzenlerdim.
app/views/shared/projects_list.haml: %ul.projects - Project.where(active: true).each do |project| = project_list_item(project) app/helpers/projects_helper.rb: def project_list_item(project) content_tag(:li, :class => 'project') do link_to project_path(project), project.name end end
Kontrolörler
Adım 4. Kontrolörlere Taşıyın
Belki de korkunç kodunuz sadece bir Görünüm endişesi değildir. Kodunuz hala kokuyorsa, Görünümlerden Denetleyicilere geçiş yapabileceğiniz sorguları arayın.

app/views/shared/projects_list.haml: %ul.projects - @projects_list.each do |project| = project_list_item(project) app/controllers/pages_controller.rb: def welcome @projects = Project.where(active: true) end
Adım 5. Denetleyici Filtreleri
Kodu bir Denetleyiciye before_filter
veya after_filter
taşımak için en belirgin neden, birden çok Denetleyici eyleminde çoğalttığınız kod içindir. Denetleyici eyleminin amacını görünümlerinizin gereksinimlerinden ayırmak istiyorsanız, kodu Denetleyici filtresine de taşıyabilirsiniz.
app/controllers/pages_controller.rb: before_filter :projects_list def welcome end def projects_list @projects = Project.where(active:true) end
Adım 6. Uygulama Denetleyicisi
Her sayfada görünmesi için kodunuza ihtiyacınız olduğunu varsayalım veya Denetleyici yardımcı işlevlerini tüm denetleyiciler için kullanılabilir hale getirmek istiyorsanız, işlevinizi ApplicationController'a taşıyabilirsiniz. Değişiklikler genel ise, uygulama düzeninizi de değiştirmek isteyebilirsiniz.
app/controllers/pages_controller.rb: def welcome end app/views/layouts/application.haml: %ul.projects - projects_list.each do |project| = project_list_item(project) app/controllers/application_controller.rb: before_filter :projects_list def projects_list @projects = Project.where(active: true) end
Modeller
Adım 7. Modeli Yeniden Düzenleme
MVC'nin sloganı şu şekildedir: Fat Model, Skinny Controllers ve Dumb Views . Modelde elimizden gelen her şeyi yeniden düzenlememiz bekleniyor ve çoğu karmaşık işlevselliğin sonunda model ilişkilendirmeleri ve model yöntemleri haline geleceği doğrudur. Modelde her zaman biçimlendirme/görüntüleme yapmaktan kaçınmalıyız, ancak verileri diğer veri türlerine dönüştürmeye izin verilir. Bu durumda, Model'e yeniden yansıtmak için en iyi şey, bir kapsam haline getirebileceğimiz where(active: true)
yan tümcesi olacaktır. Kapsam kullanmak yalnızca çağrının daha güzel görünmesini sağladığı için değerli değildir, ayrıca delete
veya outdated
gibi bir öznitelik eklemeye karar verirsek, tüm where
yan tümcelerimizi aramak yerine bu kapsamı değiştirebiliriz.
app/controllers/application_controller.rb: before_filter :projects_list def projects_list @projects = Project.active end app/models/project.rb: scope :active, where(active: true)
Adım 8. Model Filtreleri
Bu durumda bir Modelin before_save
veya after_save after_save filters
için özel bir kullanımımız yok, ancak genellikle attığım sonraki adım, işlevselliği Kontrolörler ve Model yöntemlerinden Model filtrelerine taşımak.
Diyelim ki başka bir özelliğimiz var, num_views
. num_views > 50
ise, Proje devre dışı kalır. Bu sorunu Görünümde çözebiliriz, ancak bir Görünümde veritabanı değişiklikleri yapmak uygun değildir. Bunu Denetleyicide çözebiliriz, ancak Denetleyicilerimiz mümkün olduğunca ince olmalıdır! Modelde kolayca çözebiliriz.
app/models/project.rb: before_save :deactivate_if_over_num_views def deactivate_if_over_num_views if num_views > 50 self.active = false fi end
Not: Bir Model filtresinde self.save
çağrısı yapmaktan kaçınmalısınız, çünkü bu özyinelemeli kaydetme olaylarına neden olur ve uygulamanızın veritabanı manipülasyon katmanı yine de Controller olmalıdır.
Adım 9. Kitaplıklar
Bazen, özelliğiniz kendi kitaplığını garanti edebilecek kadar büyüktür. Pek çok yerde yeniden kullanıldığından veya üzerinde ayrı ayrı geliştirme yapmak isteyeceğiniz kadar büyük olduğundan onu bir kitaplık dosyasına taşımak isteyebilirsiniz.
Kitaplık dosyalarını lib/ dizininde depolamak iyidir, ancak büyüdükçe onları gerçek bir RubyGem'e aktarabilirsiniz! Kodunuzu bir kitaplığa taşımanın büyük bir avantajı, kitaplığı modelinizden ayrı olarak test edebilmenizdir.
Her neyse, bir Proje Listesi durumunda, scope :active
çağrısını Project
modelinden bir kitaplık dosyasına taşımayı ve onu Ruby'ye geri getirmeyi haklı gösterebiliriz:
app/models/project.rb: class Project < ActiveRecord::Base include Activeable before_filter :deactivate_if_over_num_views end lib/activeable.rb: module Activeable def self.included(k) k.scope :active, k.where(active: true) end def deactivate_if_over_num_views if num_views > 50 self.active = false end end end
Not: bir Rails Model sınıfı yüklendiğinde ve sınıf kapsamında k
değişkeni olarak geçtiğinde self.included
yöntemi çağrılır.
Çözüm
Bu Ruby on Rails yeniden düzenleme eğitiminde 15 dakikadan kısa bir süreyi aldık ve bir çözüm uyguladık ve bunu özellik setine kabul edilmeye veya kaldırılmaya hazır olarak kullanıcı testi için hazırlamaya koyduk. Yeniden düzenleme sürecinin sonunda, listelenebilir, etkinleştirilebilir öğeleri birden çok Modelde uygulamak için bir çerçeve ortaya koyan ve en katı inceleme sürecini bile geçecek bir kod parçasına sahibiz.
Kendi Rails yeniden düzenleme işleminizde, bundan eminseniz ardışık düzende birkaç adımı atlamaktan çekinmeyin (örneğin, Görünümden Denetleyiciye veya Denetleyiciden Modele atlayın). Kod akışının Görünümden Modele olduğunu unutmayın.
Aptal görünmekten korkma. Modern dilleri eski CGI şablon oluşturma uygulamalarından ayıran şey, her seferinde her şeyi Doğru Şekilde yapmamız değil; yeniden düzenleme, yeniden kullanma ve çabalarımızı paylaşma zamanımızı ayırmamızdır.