Miksi Rails-moottoreita ei käytetä useammin? En tiedä vastausta, mutta luulen, että 'kaikki on moottori' -yleisön yleistäminen on piilottanut ongelma-alueet, joita ne voivat auttaa ratkaisemaan.
Erinomaista Rails Guide -dokumentaatio Rails Enginesin käytön aloittamiseksi viitataan neljään suosittuun esimerkkiin Rails Engine -sovelluksista: Forem, Devise, Spree ja RefineryCMS. Nämä ovat upeita reaalimaailman käyttötapauksia moottoreille, jotka käyttävät eri tapaa integroida Rails-sovellukseen.
Tutkimalla osia siitä, miten nämä helmet konfiguroidaan ja koostuvat, saadaan edistyneempiä Ruby on Rails -kehittäjät arvokasta tietoa siitä, mitä malleja tai tekniikoita kokeillaan luonnossa, joten kun aika tulee, sinulla voi olla muutama ylimääräinen vaihtoehto, jota voit arvioida.
Odotan sinun tuntevan päällekkäin moottorin toiminnan, joten jos sinusta tuntuu, että jokin ei ole aivan yhtä suuri, tutustu erinomaisimpaan kiskojen oppaaseen Moottoreiden käytön aloittaminen .
Etsikäämme pikemminkin, Rails-moottoreiden villissä maailmassa!
Rails-moottori, jonka tavoitteena on olla kaikkien aikojen paras pieni foorumijärjestelmä
Tämä helmi seuraa moottoreiden Rails Guide -ohjeen kirjainta. Se on huomattava esimerkki ja sen arkiston tutkiminen antaa sinulle käsityksen siitä, kuinka pitkälle voit venyttää perusasetuksia.
Se on yhden moottorin helmi, joka käyttää pari tekniikkaa integroitumaan pääsovellukseen.
module ::Forem class Engine Mielenkiintoinen osa tässä on Decorators.register!
luokan menetelmä, koristelijoiden helmi alttiina. Se kapseloi lataustiedostot, joita ei sisällytetä Rails-automaattiseen latausprosessiin. Saatat muistaa, että käytät nimenomaista require
lauseet pilaavat automaattisen uudelleenlatauksen kehitystilassa, joten tämä on hengenpelastaja! On selkeämpää käyttää oppaan esimerkkiä havainnollistaaksemme, mitä tapahtuu:
config.to_prepare do Dir.glob(Rails.root + 'app/decorators/**/*_decorator*.rb').each do |c| require_dependency(c) end end
Suurin osa Foremin kokoonpanon taikuuksista tapahtuu Forem
: n päämoduulin ylimmässä määritelmässä. Tämä tiedosto perustuu user_class
muuttuja asetetaan alustustiedostoon:
Forem.user_class = 'User'
Suoritat tämän käyttämällä mattr_accessor
mutta kaikki on Rails-oppaassa, joten en toista sitä täällä. Kun tämä on paikallaan, Forem koristaa sitten käyttäjäluokan kaikella, mitä tarvitsee sovelluksensa suorittamiseen:
module Forem class <'Forem::Post', :foreign_key => 'user_id' # ... def forem_moderate_posts? Forem.moderate_first_post && !forem_approved_to_post? end alias_method :forem_needs_moderation?, :forem_moderate_posts? # ...
Mikä osoittautuu melko paljon! Olen katkaissut enemmistön, mutta olen jättänyt yhdistysmäärittelyn ja instanssimenetelmän näyttämään sinulle tyypin rivit.
Koko tiedoston välähdys saattaa näyttää, kuinka hallittavissa osa sovelluksestasi voi olla uudelleenkäyttöä.
Koristelu on pelin nimi moottorin oletuskäytössä. Jalokivin loppukäyttäjänä voit ohittaa mallin, näkymän ja ohjaimet luomalla omat luokkaversiosi käyttämällä tiedostopolkua ja tiedostojen nimeämiskäytäntöjä, jotka on määritelty koristehelmi README: ssä. Tähän lähestymistapaan liittyy kuitenkin kustannuksia, varsinkin kun moottori saa merkittävän version päivityksen - koristeidesi ylläpitäminen voi nopeasti poistua käsistä. En mainitse Foremia täällä, uskon, että he pitävät lujasti kiinni tiukasta ydintoiminnosta, mutta pidä tämä mielessä, jos luot moottorin ja päätät tehdä kunnostuksen.
Antaa tämän yhteenvedon: tämä on Rails-oletuskoneen suunnittelumalli, joka perustuu loppukäyttäjien koristelluihin näkymiin, ohjaimiin ja malleihin sekä perusasetusten määrittämiseen alustustiedostojen avulla. Tämä toimii hyvin kohdennetuissa ja niihin liittyvissä toiminnoissa.
Motto
Löydät moottorin, joka on hyvin samanlainen kuin Rails-sovellus, jossa views
, controllers
ja models
hakemistot. Devise on hyvä esimerkki sovelluksen kapseloinnista ja kätevän integraatiopisteen paljastamisesta. Käydään läpi, miten se toimii.
Tunnistat nämä koodirivit, jos olet ollut Rails-kehittäjä yli muutaman viikon:
class User Jokainen parametri, joka välitettiin devise
method edustaa moduulia Devise-moottorissa. Näitä moduuleita on yhteensä kymmenen, jotka perivät tutusta ActiveSupport::Concern
Nämä laajentavat User
luokan kutsumalla devise
menetelmä sen soveltamisalaan.
Tämän tyyppisen integrointipisteen käyttö on erittäin joustavaa, voit lisätä tai poistaa minkä tahansa näistä parametreista muuttaaksesi toiminnon tasoa, jota moottorin tarvitset suorittavan. Se tarkoittaa myös, että sinun ei tarvitse koodata sitä mallia, jota haluat käyttää alustustiedostossa, kuten Rails Guide on Engines ehdottaa. Toisin sanoen tämä ei ole välttämätöntä:
Devise.user_model = 'User'
Tämä abstraktio tarkoittaa myös sitä, että voit soveltaa tätä useampaan kuin yhteen käyttäjämalliin samassa sovelluksessa (esimerkiksi | admin
ja user
), kun taas määritystiedostotapa jättää sinut sidottuun yhteen malliin todennuksella. Tämä ei ole suurin myyntivaltti, mutta se kuvaa erilaista tapaa ratkaista ongelma.
Devise jatkuu ActiveRecord::Base
omalla moduulillaan, joka sisältää devise
menetelmän määritelmä:
# lib/devise/orm/active_record.rb ActiveRecord::Base.extend Devise::Models
Mikä tahansa luokka, joka perii ActiveRecord::Base
on nyt pääsy luokkamenetelmiin, jotka on määritelty Devise::Models
:
#lib/devise/models.rb module Devise module Models # ... def devise(*modules) selected_modules = modules.map(&:to_sym).uniq # ... selected_modules.each do |m| mod = Devise::Models.const_get(m.to_s.classify) if mod.const_defined?('ClassMethods') class_mod = mod.const_get('ClassMethods') extend class_mod # ... end include mod end end # ... end end
(Olen poistanut paljon koodia (# ...
) korostaaksesi tärkeät osat.)
Parafraseeraa koodi jokaiselle moduulin nimelle, joka välitetään devise
menetelmä olemme:
- ladataan määrittelemämme moduuli, joka asuu
Devise::Models
(Devise::Models.const_get(m.to_s.classify
) - jatkamalla
User
luokka ClassMethods
moduuli, jos sillä on sellainen - sisällytä määritetty moduuli (
include mod
) lisätäksesi sen esiintymämenetelmät luokkaan, joka kutsuu devise
menetelmä (User
)
Jos haluat luoda moduulin, joka voidaan ladata tällä tavalla, sinun on varmistettava, että se noudattaa tavanomaista ActiveSupport::Concern
käyttöliittymä, mutta nimitila sen alla Devise:Models
koska täältä haluamme hakea vakion:
module Devise module Models module Authenticatable extend ActiveSupport::Concern included do # ... end module ClassMethods # ... end end end end
Huh huh.
Jos olet käyttänyt Railsin huolia aiemmin ja kokenut niiden käyttökelpoisuuden, voit arvostaa tämän lähestymistavan hienostuneisuutta. Lyhyesti sanottuna toiminnallisuuden hajottaminen tällä tavalla tekee testaamisesta helpompaa erottamalla ActiveRecord
malli, ja sillä on pienempi yleiskustannus kuin Foremin oletusarvoinen malli toimintojen laajentamisen suhteen.
Tämä malli koostuu toimintojesi jakamisesta Rails-huolenaiheiksi ja konfigurointipisteen paljastamisesta sisällyttämään tai sulkemaan ne pois tietyllä laajuudella. Tällä tavalla muodostettu moottori on kätevä loppukäyttäjälle - mikä vaikuttaa Devisen menestykseen ja suosioon. Ja nyt osaat myös tehdä sen!
Spree
Täydellinen avoimen lähdekoodin verkkokaupparatkaisu Ruby on Rails -sovellukselle
Spree koki valtavan ponnistelun saadakseen monoliittisen sovelluksensa hallintaan siirtymällä käyttämään moottoreita. Arkkitehtuurisuunnittelu, jonka kanssa he nyt liikkuvat, on 'Spree' -helmi, joka sisältää monia moottorin helmiä.
Nämä moottorit luovat osioita käyttäytymisessä, jonka olet tottunut näkemään monoliittisessa sovelluksessa tai hajautettuna sovelluksiin:
- spree_api (RESTful-sovellusliittymä)
- spree_frontend (Käyttäjäkohtaiset komponentit)
- spree_backend (Järjestelmänvalvojan alue)
- spree_cmd (komentorivityökalut)
- spree_core (Mallit ja postittajat, Spree-laitteen peruskomponentit, joita ei voi käyttää ilman)
- spree_sample (näytetiedot)
Sisältävä helmi ompelee nämä yhteen, jolloin kehittäjällä on mahdollisuus valita vaadittavan toiminnallisuuden taso. Voit esimerkiksi suorittaa vain spree_core
Moottori ja kääri oma käyttöliittymä sen ympärille.
Tärkein Spree-helmi vaatii nämä moottorit:
# lib/spree.rb require 'spree_core' require 'spree_api' require 'spree_backend' require 'spree_frontend'
Jokaisen moottorin on sitten mukautettava engine_name
ja root
polku (jälkimmäiset osoittavat yleensä ylätason helmille) ja määrittää itsensä kytkemällä alustusprosessiin:
# api/lib/spree/api/engine.rb require 'rails/engine' module Spree module Api class Engine :load_config_initializers do |app| app.config.spree = Spree::Core::Environment.new end # ... end end end
Et voi tunnistaa tätä alustusmenetelmää: se on osa Railtie
ja on koukku, joka antaa sinulle mahdollisuuden lisätä tai poistaa vaiheita Rails-kehyksen alustusesta. Spree luottaa tähän koukkuun voimakkaasti, kun se määrittää monimutkaisen ympäristön kaikille moottoreilleen.
Käyttämällä yllä olevaa esimerkkiä ajon aikana pääset asetuksiisi pääsemällä ylimmälle tasolle Rails
vakio:
Rails.application.config.spree
Tämän yllä olevan Rails-moottorin suunnittelumallin oppaan avulla voimme kutsua sitä päiväksi, mutta Sprellä on paljon hämmästyttävää koodia, joten tutustutaanpa siihen, miten he käyttävät alustusta jakamaan kokoonpanot moottoreiden ja pääkiskosovelluksen välillä.
Spree: llä on monimutkainen preferenssijärjestelmä, jonka se lataa lisäämällä vaiheen alustusprosessiin:
# api/lib/spree/api/engine.rb initializer 'spree.environment', :before => :load_config_initializers do |app| app.config.spree = Spree::Core::Environment.new end
Täällä kiinnitämme app.config.spree
uusi Spree::Core::Environment
ilmentymä. Rails-sovelluksessa pääset tähän Rails.application.config.spree
-palvelun kautta mistä tahansa - malleista, ohjaimista, näkymistä.
Siirtymällä alaspäin Spree::Core::Environment
luomamme luokka näyttää tältä:
module Spree module Core class Environment attr_accessor :preferences def initialize @preferences = Spree::AppConfiguration.new end end end end
Se paljastaa :preferences
muuttuja asetettu uuteen Spree::AppConfiguration
-esiintymään luokka, joka puolestaan käyttää preference
Preferences::Configuration
-menetelmässä määritelty menetelmä luokka asettaa asetuksia oletusasetuksilla yleisen sovelluksen kokoonpanolle:
module Spree class AppConfiguration En näytä Preferences::Configuration
tiedosto, koska se vie vähän selitystä, mutta lähinnä se on syntaktinen sokeri asetusten saamiseksi ja asettamiseksi. (Todellisuudessa tämä on sen toimintojen yksinkertaistaminen, koska preferenssijärjestelmä tallentaa muut kuin oletusarvot tietokannan olemassa oleville tai uusille asetuksille mille tahansa ActiveRecord
luokalle, jolla on :preference
sarake - mutta sinun ei tarvitse tietää sitä.)
Tässä on yksi näistä vaihtoehdoista toiminnassa:
module Spree class Calculator Laskimet hallitsevat kaikenlaisia asioita Spreessä - lähetyskustannukset, tarjoukset ja tuotteiden hinnanmuutokset -, joten mekanismi, jolla ne voidaan vaihtaa tällä tavoin, lisää moottorin laajennettavuutta.
Yksi monista tavoista, joilla voit ohittaa näiden asetusten oletusasetukset, on Rails-sovelluksen alustuksessa:
# config/initializergs/spree.rb Spree::Config do |config| config.admin_interface_logo = 'company_logo.png' end
Jos olet lukenut RailsGuide on moottorit , harkitsivat heidän suunnittelumallejaan tai rakensivat itse moottorin, tiedät, että on merkityksetöntä paljastaa seteri alustustiedostossa jonkun käytettäväksi. Joten saatat ihmetellä, miksi kaikki hässäkkää asennus- ja mieltymysjärjestelmän kanssa? Muista, että preferenssijärjestelmä ratkaisee Spree-verkkotunnuksen ongelman. Liittäminen alustusprosessiin ja pääsy Rails-kehykseen voi auttaa sinua täyttämään vaatimukset ylläpidettävällä tavalla.
Tämä moottorin suunnittelumalli keskittyy Rails-kehyksen käyttämiseen vakiona sen monien liikkuvien osien välillä tallentaakseen asetukset, jotka eivät (yleensä) muutu ajon aikana, mutta muuttuvat sovellusasennusten välillä.
Jos olet joskus yrittänyt valkomerkki kiskot -sovelluksella, saatat olla perehtynyt tähän asetusskenaarioon ja olet kokenut mutkikkaita tietokannan ”asetustaulukoita” jokaisen uuden sovelluksen pitkässä asennusprosessissa. Nyt tiedät, että käytettävissä on erilainen polku ja se on mahtavaa - viisi korkeaa!
JalostamoCMS
Konfiguraatio kokoonpanosta kukaan? Rails-moottorit voivat ajoittain vaikuttaa enemmän kuin kokoonpanoharjoittelu, mutta RefineryCMS muistaa osan Rails-taikuudesta. Tämä on sen koko sisältö lib
hakemisto:
# lib/refinerycms.rb require 'refinery/all' # lib/refinery/all.rb %w(core authentication dashboard images resources pages).each do |extension| require 'refinerycms-#{extension}' end
Vau. Jos et voi sanoa tällä tavalla, jalostamon tiimi tietää todella, mitä he tekevät. He liikkuvat käsitteellä extension
joka on pohjimmiltaan toinen moottori. Kuten Spree, sillä on kattava ompeleen helmi, mutta siinä käytetään vain kahta ompelua, ja se kokoaa kokoelman moottoreita tarjoamaan kaikki toiminnot.
Laajennuksia luovat myös Engine-käyttäjät luodakseen oman CMS-ominaisuuksiensa yhdistämisen bloggaamista, uutisia, salkkuja, suosituksia, tiedusteluja jne. Varten (tämä on pitkä luettelo), jotka kaikki kiinnittyvät RefineryCMS: n ytimeen.
Tämä muotoilu voi kiinnittää huomiosi modulaariseen lähestymistapaansa, ja jalostamo on loistava esimerkki tästä Rails-suunnittelukuviosta. 'Kuinka se toimii?' Kuulen sinun kysyvän.
core
moottori kartoittaa muutaman koukun muille moottoreille käytettäväksi:
# core/lib/refinery/engine.rb module Refinery module Engine def after_inclusion(&block) if block && block.respond_to?(:call) after_inclusion_procs << block else raise 'Anything added to be called after_inclusion must be callable (respond to #call).' end end def before_inclusion(&block) if block && block.respond_to?(:call) before_inclusion_procs << block else raise 'Anything added to be called before_inclusion must be callable (respond to #call).' end end private def after_inclusion_procs @@after_inclusion_procs ||= [] end def before_inclusion_procs @@before_inclusion_procs ||= [] end end end
Kuten näet before_inclusion
ja after_inclusion
vain tallentaa luettelon procs, jotka suoritetaan myöhemmin. Jalostamon sisällyttämisprosessi laajentaa tällä hetkellä ladattuja Rails-sovelluksia jalostamon ohjaimilla ja avustajilla. Tässä yksi toiminnassa:
# authentication/lib/refinery/authentication/engine.rb before_inclusion do [Refinery::AdminController, ::ApplicationController].each do |c| Refinery.include_once(c, Refinery::AuthenticatedSystem) end end
Olen varma, että olet lisännyt todennustavat ApplicationController
ja AdminController
aiemmin tämä on ohjelmallinen tapa tehdä se.
Authentication Engine -tiedoston loppuosan tarkasteleminen auttaa meitä keräämään muutamia muita avainkomponentteja:
module Refinery module Authentication class Engine <::Rails::Engine extend Refinery::Engine isolate_namespace Refinery engine_name :refinery_authentication config.autoload_paths += %W( #{config.root}/lib ) initializer 'register refinery_user plugin' do Refinery::Plugin.register do |plugin| plugin.pathname = root plugin.name = 'refinery_users' plugin.menu_match = %r{refinery/users$} plugin.url = proc { Refinery::Core::Engine.routes.url_helpers.admin_users_path } end end end config.after_initialize do Refinery.register_extension(Refinery::Authentication) end # ... end end
Hupun alla jalostamon laajennuksissa käytetään Plugin
järjestelmään. initializer
askel näyttää tutulta Spree-koodianalyysistä, täällä vain tapaavat register
menetelmien vaatimukset lisätään Refinery::Plugins
-luetteloon että core
laajennus seuraa ja Refinery.register_extension
Lisää vain moduulin nimi luokkaan tallennettuun luetteloon.
Tässä on järkyttävä: Refinery::Authentication
luokka on todella kääre Devisen ympärillä, jossa on joitain mukautuksia. Joten se on kilpikonnia koko matkan!
Laajennukset ja laajennukset ovat käsitteitä, joita jalostamo on kehittänyt tukemaan rikkaita minikiskosovellusten ja työkalujen ekosysteemejä - ajattele rake generate refinery:engine
. Suunnittelumalli eroaa Spree-ohjelmasta asettamalla Rails-moottorin ympärille ylimääräisen sovellusliittymän, joka auttaa niiden koostumuksen hallinnassa.
'Rails Way' -idioomi on jalostamon ytimessä, yhä enemmän heidän mini-kiskosovelluksissaan, mutta ulkopuolelta et tiedä sitä. Rajojen suunnittelu sovelluksen kokoonpanotasolla on yhtä tärkeää, mahdollisesti enemmän, kuin puhtaan API: n luominen Rails-sovelluksissa käytettäville luokille ja moduuleille.
Koodin kääriminen, jota sinulla ei ole suoraa hallintaa, on yleinen malli, se on ennakointi lyhentämään ylläpitoaikaa, kun koodi muuttuu, rajoittamalla paikkojen määrää, jotka sinun on tehtävä muutoksia päivitysten tukemiseksi. Tämän tekniikan soveltaminen osiointitoimintojen rinnalle luo joustavan alustan sävellyksille, ja tässä on todellinen esimerkki suoraan nenänne alla - täytyy rakastaa avointa lähdekoodia!
Johtopäätös
Olemme nähneet neljä lähestymistapaa Rails-moottorimallien suunnitteluun analysoimalla suosittuja jalokiviä, joita käytetään todellisissa sovelluksissa. Kannattaa lukea heidän arkistojensa kautta oppia runsaasta kokemuksesta, jota on jo sovellettu ja toistettu. Seiso jättiläisten harteilla.
Tässä Rails-oppaassa olemme keskittyneet suunnittelumalleihin ja tekniikoihin Rails-moottoreiden ja niiden loppukäyttäjien Rails-sovellusten integroimiseksi, jotta voit lisätä näiden tietojen Rails-työkaluvyö .
Toivon, että olet oppinut yhtä paljon kuin minä tämän koodin tarkistamisesta ja tunnet innoituksen antaa Rails-moottoreille mahdollisuus, kun ne sopivat laskuun. Suuri kiitos ylläpitäjille ja osallistujille tarkistamiemme helmiin. Hyviä ihmisiä!
Liittyvät: Aikaleiman katkaisu: Ruby on Rails ActiveRecord Tale