socialgekon.com
  • Hoved
  • Fremtidens Arbeid
  • Livsstil
  • Designerliv
  • Ux Design
Baksiden

En guide til skinnemotorer i naturen: virkelige verdenseksempler på skinnemotorer i aksjon

Hvorfor brukes ikke skinnemotorer oftere? Jeg vet ikke svaret, men jeg tror at generaliseringen av 'Everything is an Engine' har skjult problemdomenene de kan hjelpe til med å løse.

Det suverene Rails Guide dokumentasjon for å komme i gang med Rails Motorer refererer til fire populære eksempler på Rails Engine-implementeringer: Forem, Devise, Spree og RefineryCMS. Dette er fantastiske virkelige verdensbrukstilfeller for motorer som hver bruker en annen tilnærming til integrering med et Rails-program.

Hver Rails-guide skal dekke temaet for Rails-motorens designmønstre og deres eksempler.



Å undersøke deler av hvordan disse perlene er konfigurert og komponert vil gi avansert Ruby on Rails-utviklere verdifull kunnskap om hvilke mønstre eller teknikker som blir prøvd ut i naturen, så når tiden kommer kan du ha noen ekstra muligheter å evaluere mot.

Jeg forventer at du har en kortvarig kjennskap til hvordan en motor fungerer, så hvis du føler at noe ikke helt legger opp, kan du lese den mest fremragende Rails Guide Komme i gang med motorer .

Uten videre, la oss våge oss i den ville verden av Rails-motoreksempler!

Gå

En motor for Rails som har som mål å være det beste lille forum-systemet noensinne

Denne perlen følger retningen til Rails Guide on Engines til punkt og prikke. Det er et betydelig eksempel, og å lese gjennom depotet ditt vil gi deg en ide om hvor langt du kan strekke det grunnleggende oppsettet.

Det er en enmotors perle som bruker et par teknikker for å integrere med hovedapplikasjonen.

module ::Forem class Engine

Den interessante delen her er Decorators.register! klassemetode, eksponert av dekoratørperlen. Den innkapsler innlasting av filer som ikke ville være inkludert i Rails autoloading-prosessen. Du husker kanskje at du bruker eksplisitt require uttalelser ødelegger automatisk omlasting i utviklingsmodus, så dette er en livredder! Det vil være tydeligere å bruke eksemplet fra guiden for å illustrere hva som skjer:

config.to_prepare do Dir.glob(Rails.root + 'app/decorators/**/*_decorator*.rb').each do |c| require_dependency(c) end end

Det meste av magien for Forems konfigurasjon skjer i definisjonen av Forem i hovedmodulen. Denne filen er avhengig av en user_class variabel som blir satt i en initialiseringsfil:

Forem.user_class = 'User'

Du oppnår dette ved hjelp av mattr_accessor men det er alt i Rails Guide, så jeg vil ikke gjenta det her. Med dette på plass, dekorerer Forem brukerklassen med alt den trenger for å kjøre applikasjonen:

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? # ...

Noe som viser seg å være ganske mye! Jeg har klippet ut flertallet, men har lagt igjen en tilknytningsdefinisjon samt en forekomstmetode for å vise deg hvilken type linjer du kan finne der inne.

Et glimt av hele filen kan vise deg hvor håndterbar porting av en del av applikasjonen din for gjenbruk til en motor kan være.

Dekorering er navnet på spillet i standard motorbruk. Som sluttbruker av perlen kan du overstyre modell, visning og kontrollere ved å lage dine egne versjoner av klassene ved hjelp av filstier og filnavnkonvensjoner som er lagt ut i dekoratørperlen README. Det er imidlertid en kostnad forbundet med denne tilnærmingen, spesielt når motoren får en større versjonsoppgradering - vedlikeholdet av å holde dekorasjonene dine i bruk kan raskt komme ut av hånden. Jeg siterer ikke Forem her, jeg tror de er standhaftige i å følge en sammensveiset kjernefunksjonalitet, men husk dette hvis du lager en motor og bestemmer deg for å gå til en revisjon.

La oss oppsummere denne: dette er standard Rails-motordesignmønster som stole på at sluttbrukere dekorerer visninger, kontrollere og modeller, sammen med å konfigurere grunnleggende innstillinger via initialiseringsfiler. Dette fungerer bra for veldig fokusert og relatert funksjonalitet.

Motto

En fleksibel autentiseringsløsning for Rails

Du vil finne at en motor er veldig lik et Rails-program, med views, controllers og models kataloger. Devise er et godt eksempel på å kapsle inn en applikasjon og avsløre et praktisk integreringspunkt. La oss gå gjennom hvordan akkurat det fungerer.

Du vil gjenkjenne disse kodelinjene hvis du har vært Rails-utvikler i mer enn noen få uker:

class User

Hver parameter sendes til devise metoden representerer en modul i Devise Engine. Det er ti av disse modulene som arver fra det kjente ActiveSupport::Concern. Disse utvider User klasse ved å påkalle devise metode innenfor sitt virkeområde.

Å ha denne typen integreringspunkt er veldig fleksibel, du kan legge til eller fjerne noen av disse parameterne for å endre nivået på funksjonalitet du trenger at motoren skal utføre. Det betyr også at du ikke trenger å hardkode hvilken modell du vil bruke i en initialiseringsfil, som foreslått av Rails Guide on Engines. Dette er med andre ord ikke nødvendig:

Devise.user_model = 'User'

Denne abstraksjonen betyr også at du kan bruke dette på mer enn en brukermodell i samme applikasjon (admin og user for eksempel), mens konfigurasjonsfiltilnærmingen vil la deg være bundet til en enkelt modell med autentisering. Dette er ikke det største salgsargumentet, men det illustrerer en annen måte å løse et problem på.

Devise strekker seg ActiveRecord::Base med sin egen modul som inkluderer devise metode definisjon:

# lib/devise/orm/active_record.rb ActiveRecord::Base.extend Devise::Models

Enhver klasse som arver fra ActiveRecord::Base vil nå ha tilgang til klassemetodene som er definert i 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

(Jeg har fjernet mye kode (# ...) for å markere viktige deler.)

Omformulere koden for hvert modulnavn sendt til devise metoden vi er:

  • laster inn modulen vi spesifiserte som lever under Devise::Models (Devise::Models.const_get(m.to_s.classify)
  • utvide User klasse med ClassMethods modul hvis den har en
  • inkluderer den spesifiserte modulen (include mod) for å legge til instansmetodene til klassen som kaller devise metode (User)

Hvis du ønsker å lage en modul som kan lastes inn på denne måten, må du sørge for at den følger det vanlige ActiveSupport::Concern grensesnitt, men navneområdet under Devise:Models da det er her vi ønsker å hente konstanten:

module Devise module Models module Authenticatable extend ActiveSupport::Concern included do # ... end module ClassMethods # ... end end end end

Puh.

Hvis du har brukt Rails ’Concerns før og opplevd gjenbrukbarheten de har råd til, kan du sette pris på det fine med denne tilnærmingen. Kort sagt, å bryte opp funksjonalitet på denne måten gjør testing enklere ved å bli abstrahert fra en ActiveRecord modell, og har lavere overhead enn standardmønsteret som brukes av Forem når det gjelder utvidelse av funksjonalitet.

Dette mønsteret består i å dele opp funksjonaliteten din i Rails Concerns og eksponere et konfigurasjonspunkt for å inkludere eller ekskludere disse innenfor et gitt omfang. En motor dannet på denne måten er praktisk for sluttbrukeren - en medvirkende faktor til suksessen og populariteten til Devise. Og nå vet du hvordan du gjør det også!

Spree

En komplett åpen kildekode-e-handelsløsning for Ruby on Rails

Spree gjennomgikk et kolossalt forsøk på å bringe deres monolitiske applikasjon under kontroll med å gå over til å bruke motorer. Arkitekturdesignet de nå ruller med er en 'Spree' -perle som inneholder mange motorperler.

Disse motorene lager partisjoner i atferd du kan være vant til å se i en monolitisk applikasjon eller spredt over applikasjoner:

  • spree_api (RESTful API)
  • spree_frontend (Brukervendte komponenter)
  • spree_backend (Administrasjonsområde)
  • spree_cmd (kommandolinjeverktøy)
  • spree_core (Models & Mailers, de grunnleggende komponentene i Spree som den ikke kan kjøre uten)
  • spree_sample (eksempeldata)

Den omfattende perlen syr disse sammen, slik at utvikleren har et valg i funksjonsnivået å kreve. For eksempel kan du løpe med bare spree_core Motorer og vikle ditt eget grensesnitt rundt det.

Den viktigste Spree-perlen krever disse motorene:

# lib/spree.rb require 'spree_core' require 'spree_api' require 'spree_backend' require 'spree_frontend'

Hver motor må deretter tilpasse sin engine_name og root sti (sistnevnte peker vanligvis på perlen på øverste nivå) og konfigurerer seg ved å koble til initialiseringsprosessen:

# 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

Du kan kanskje ikke gjenkjenne denne initialiseringsmetoden: den er en del av Railtie og er en krok som gir deg muligheten til å legge til eller fjerne trinn fra initialiseringen av Rails-rammeverket. Spree stoler sterkt på denne kroken for å konfigurere det komplekse miljøet for alle motorene.

Ved å bruke eksemplet ovenfor under kjøretid, vil du få tilgang til innstillingene dine ved å gå til toppnivået Rails konstant:

Rails.application.config.spree

Med denne Rails-motordesignguiden ovenfor kan vi kalle det en dag, men Spree har massevis av fantastisk kode, så la oss dykke inn i hvordan de bruker initialisering for å dele konfigurasjon mellom motorer og hovedsporapplikasjonen.

Spree har et komplekst preferansesystem som det lastes inn ved å legge til et trinn i initialiseringsprosessen:

# api/lib/spree/api/engine.rb initializer 'spree.environment', :before => :load_config_initializers do |app| app.config.spree = Spree::Core::Environment.new end

Her legger vi til app.config.spree et nytt Spree::Core::Environment forekomst. Innenfor skinnesøknaden vil du kunne få tilgang til dette via Rails.application.config.spree hvor som helst - modeller, kontrollere, visninger.

Fortsetter nedover, Spree::Core::Environment klasse vi lager ser slik ut:

module Spree module Core class Environment attr_accessor :preferences def initialize @preferences = Spree::AppConfiguration.new end end end end

Den avslører et :preferences variabel satt til en ny forekomst av Spree::AppConfiguration klasse, som igjen bruker en preference metoden definert i Preferences::Configuration klasse for å angi alternativer med standardinnstillinger for den generelle applikasjonskonfigurasjonen:

module Spree class AppConfiguration

Jeg vil ikke vise Preferences::Configuration fil fordi det tar litt forklaring, men egentlig er det syntaktisk sukker for å få og stille inn preferanser. (I sannhet er dette en forenkling av funksjonaliteten, ettersom preferansesystemet vil lagre andre enn standardverdier for eksisterende eller nye preferanser i databasen, for alle ActiveRecord klasser med en :preference kolonne - men du trenger ikke å vite det.)

Her er et av disse alternativene i aksjon:

module Spree class Calculator

Kalkulatorer kontrollerer alle slags ting i Spree - fraktkostnader, kampanjer, produktjusteringer - så å ha en mekanisme for å bytte dem ut på denne måten øker motorens utvidbarhet.

En av de mange måtene du kan overstyre standardinnstillingene for disse innstillingene, er i en initialisering i hovedsporet:

# config/initializergs/spree.rb Spree::Config do |config| config.admin_interface_logo = 'company_logo.png' end

Hvis du har lest gjennom RailsGuide on Motors , vurderte designmønstrene deres eller bygde en motor selv, vil du vite at det er trivielt å eksponere en setter i en initialiseringsfil som noen kan bruke. Så du lurer kanskje på hvorfor alt oppstyret med oppsett- og preferansesystemet? Husk at preferansesystemet løser et domeneproblem for Spree. Å koble seg til initialiseringsprosessen og få tilgang til Rails-rammeverket kan hjelpe deg med å oppfylle dine behov på en vedlikeholdbar måte.

Dette motordesignmønsteret fokuserer på å bruke Rails-rammeverket som konstanten mellom de mange bevegelige delene for å lagre innstillinger som ikke (generelt) endres ved kjøretid, men som skifter mellom applikasjonsinstallasjoner.

Hvis du noen gang har prøvd det hvit etikett et Rails-program, kan du være kjent med dette innstillingsscenarioet, og har følt smerten ved innviklede databasens 'innstillinger' -tabeller i løpet av en lang installasjonsprosess for hvert nytt program. Nå vet du at en annen vei er tilgjengelig, og det er kjempebra - high five!

RaffineriCMS

Et open source content management system for Rails

Konvensjon om konfigurasjon noen? Rails Engines kan definitivt virke mer som en øvelse i konfigurasjon til tider, men RefineryCMS husker noe av den Rails-magien. Dette er hele innholdet i det lib katalog:

# lib/refinerycms.rb require 'refinery/all' # lib/refinery/all.rb %w(core authentication dashboard images resources pages).each do |extension| require 'refinerycms-#{extension}' end

Wow. Hvis du ikke vet hva dette vet, vet raffineriteamet virkelig hva de gjør. De ruller med konseptet med en extension som egentlig er en annen motor. I likhet med Spree har den en omfattende sømperle, men bruker bare to masker, og samler en samling motorer for å levere sitt fulle sett med funksjonalitet.

Utvidelser er også opprettet av brukere av Engine, for å lage sin egen sammenblanding av CMS-funksjoner for blogging, nyheter, portefølje, attester, henvendelser osv. (Det er en lang liste), alt sammenkoblet til kjernen RefineryCMS.

Dette designet kan få oppmerksomhet for sin modulære tilnærming, og Refinery er et godt eksempel på dette Rails designmønsteret. 'Hvordan virker det?' Jeg hører deg spørre.

core motoren kartlegger noen kroker for de andre motorene å bruke:

# 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

Som du kan se before_inclusion og after_inclusion bare lagre en liste over produkter som kjøres senere. Inkluderingsprosessen for raffineriet utvider de nåværende lastede Rails-applikasjonene med Refinery's kontrollere og hjelpere. Her er en i aksjon:

# authentication/lib/refinery/authentication/engine.rb before_inclusion do [Refinery::AdminController, ::ApplicationController].each do |c| Refinery.include_once(c, Refinery::AuthenticatedSystem) end end

Jeg er sikker på at du har lagt autentiseringsmetoder til ApplicationController og AdminController før, er dette en programmatisk måte å gjøre det på.

Når vi ser på resten av Authentication Engine-filen, kan vi finne et par andre viktige komponenter:

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

Under panseret bruker raffineriutvidelser en Plugin system. initializer trinn vil se kjent ut fra Spree-kodeanalysen, her var det bare å møte register metodekrav som skal legges til i listen over Refinery::Plugins at core utvidelse holder oversikt over, og Refinery.register_extension bare legger til modulnavnet i en liste lagret i en klassetilgang.

Her er en sjokkerer: Refinery::Authentication klasse er virkelig en innpakning rundt Devise, med litt tilpasning. Så det er skilpadder helt ned!

Utvidelsene og pluginene er konsepter Refinery har utviklet for å støtte sitt rike økosystem med mini-rails-apper og verktøy - tenk rake generate refinery:engine. Designmønsteret her skiller seg fra Spree ved å pålegge en ekstra API rundt Rails Engine for å hjelpe til med å administrere sammensetningen.

“The Rails Way” -idiomet er kjernen i Refinery, stadig mer til stede i deres mini-rails-apper, men utenfra ville du ikke vite det. Å designe grenser på applikasjonssammensetningsnivå er like viktig, muligens mer, enn å lage et rent API for dine klasser og moduler som brukes i Rails-applikasjonene dine.

Innpakningskode som du ikke har direkte kontroll over er et vanlig mønster. Det er en framsyn for å redusere vedlikeholdstiden for når den koden endres, og begrenser antall steder du trenger å gjøre endringer for å støtte oppgraderinger. Bruk av denne teknikken sammen med partisjoneringsfunksjonalitet skaper en fleksibel plattform for komposisjon, og her er et eksempel fra den virkelige verden rett under nesen din - må elske åpen kildekode!

Konklusjon


Vi har sett fire tilnærminger for å designe Rails-motormønstre ved å analysere populære perler som brukes i virkelige applikasjoner. Det er verdt å lese gjennom arkivene deres for å lære av et vell av erfaring som allerede er brukt og gjentatt. Stå på skuldrene til kjemper.

I denne Rails-guiden har vi fokusert på designmønstre og teknikker for integrering av Rails Engines og deres sluttbrukers Rails-applikasjoner, slik at du kan legge til kunnskapen om disse til din Skinner verktøybelte .

Jeg håper du har lært like mye som jeg av å ha gjennomgått denne koden og føler meg inspirert til å gi Rails Engines en sjanse når de passer til regningen. En stor takk til vedlikeholdere og bidragsytere til perlene vi gjennomgikk. Stor jobb folk!

I slekt: Avkorting av tidsstempel: En Ruby on Rails ActiveRecord Tale

10 overraskende iPhone-fotograferingstips for nybegynnere

Skyting

10 overraskende iPhone-fotograferingstips for nybegynnere
JavaScript-prototypkjeder, omfangskjeder og ytelse: Hva du trenger å vite

JavaScript-prototypkjeder, omfangskjeder og ytelse: Hva du trenger å vite

Web Front-End

Populære Innlegg
C Corp vs. S Corp, partnerskap, eierforetak og LLC: Hva er den beste forretningsenheten?
C Corp vs. S Corp, partnerskap, eierforetak og LLC: Hva er den beste forretningsenheten?
iOS ARKit-opplæring: Drawing in the Air with Bare Fingers
iOS ARKit-opplæring: Drawing in the Air with Bare Fingers
Zen av devRant
Zen av devRant
Hvordan gjøre bildet om til en skisse eller tegning
Hvordan gjøre bildet om til en skisse eller tegning
Til designere med kjærlighet (et brev fra en frontend-utvikler)
Til designere med kjærlighet (et brev fra en frontend-utvikler)
 
Objektdeteksjon ved hjelp av OpenCV og Swift
Objektdeteksjon ved hjelp av OpenCV og Swift
Hensyn til å skaffe ditt eget private equity-fond
Hensyn til å skaffe ditt eget private equity-fond
Front-End Frameworks: Løsninger eller store problemer?
Front-End Frameworks: Løsninger eller store problemer?
Samarbeidsdesign: En guide til vellykket bedriftsproduktdesign
Samarbeidsdesign: En guide til vellykket bedriftsproduktdesign
ARM-servere: Mobil CPU-arkitektur for datasentre?
ARM-servere: Mobil CPU-arkitektur for datasentre?
Kategorier
Datavitenskap Og DatabaserTeknologiRise Of RemoteVerktøy Og OpplæringsprogrammerLivsstilProduktets LivssyklusMobil DesignFremtidens ArbeidPlanlegging Og PrognoserPeople & Teams

© 2023 | Alle Rettigheter Reservert

socialgekon.com