Laikas - pinigai. Visi žinome šią taisyklę. Naudojame karkasus, TVS, galų gale bibliotekas. Viskas tam, jog sumažintume laiką, kurį praleidžiame programuodami portalą ar užduotį, redukuoti jo apimtį ir tiesiog atsikratyti dalies nuobodaus darbo perkeliant jį mūsų naudojamų pagalbinių priemonių kūrėjams.

(toliau Rails) karkasas palengvino kai kurių programuotojų kasdienybę - pasiūlė sprendimus, kurie taupo laiką, tuo pačiu išlikdami lankstūs. Vienas iš nuostabiausių jo aspektų - programuotojų bendruomenė, suvienyta idėjos, jog programavimas neturėtų būti kančia ir tonos SQL ar XML konfigūracinių failų rašymas. Taip gimė tūkstančiai įvairių įskiepių (angl. - plugins), kurie palengvino Jūsų programavimo darbus, pradedant nuo failų siuntimo į serverį, baigiant foniniame režime veikiančiais procesais (kaip tarkim masiniu laiškų išsiuntimu).

Vienas iš tų įskiepių - Rails Engines.

Daugiau nebereikės rašyti to paties du kartus! Prižadu. Norite sužinoti kaip?

Tam, jog suprastumėte, kas čia vyksta, Jums reikės žinoti keletą konceptualių tiesų:

vs Components

Komponentinio programavimo būdo (naudojamas, pavyzdžiui, ASP.NET arba PHP5 karkase PRADO) idėja - kodas turėtų būti rašomas vieną kartą - naudojamas daug.

Komponentinio programavimo paradigma neapibrėžia, kaip kodas turėtų būti išdėstytas ar struktūrizuotas, ji apibrėžia kaip jis turi būti naudojamas. Paprasčiausias komponentinio programavimo pavyzdys:

<?= displayComponent('calendar', array(
  'startDate' => 1970,
  'endDate' => CALENDAR_DATE_NOW,
  'weekStart' => CALENDAR_DAY_MONDAY
) ); ?>

Kaip matome programuotojui vartojančiam komponentą lieka jį tik įterpti ten, kur jam reikia. Atrodytų viskas labai gerai ir gražu. Minusai?

  • Ribota laisvė modifikuoti komponento elgseną. Darant prielaidą, jog komponento keisti negalime, turime tik tiek laisvės, kiek mums davė komponento kūrėjas.
  • Komponento programinė architektūra ir kodo aiškumas paliekamas programuotojo sąžinei. Dažniausiai ten lieka toooks “bardakas”, jog kraupu žiūrėti. Paprastai taip nutinka dėl netinkamų ar iš viso nesančių priemonių, kurios padarytų tą struktūrizavimą paprastu ir lengvai implementuojamu. Taipogi nesame garantuoti, jog net ir tam pačiam karkasui skirti komponentai bus struktūrizuoti vienodai.

Rails Engines - komponentizuotas ?

yra labai gerai ir gražu kol neprieiname prie pakartotinio kodo panaudojimo. Vienas iš sprendimo būdų, kuriuos pasitelkė Rails kūrėjai, bandė šią bėdą spręsti “scaffold’ingu” (automatiniu puslapių generavimu pagal paduotus parametrus ir duombazės struktūrą. Kaip prisipažįsta patys Rails kūrėjai - tai dažniausiai tinka tik pradiniam projekto etapui, vėliau tenka viską perrašyti) bei generatoriais (mažytėmis programėlėmis, kurios pagal duotus parametrus sugeneruoja architektūros kodą tam tikram tikslui. Pavyzdžiui, LoginGenerator.

Abu sprendimai yra truputį netikę: scaffold’ingas nėra pakankamai lankstus ir realiai panaudojamas, generatoriai prišiukšlina aplikacijos išeities kodą ir, vėlgi, dažniausiai tenka tai, ką jie sugeneravo, mėsinėti ir pritaikyti sau (atmetus atvejus, kai generatorius rašėm patys). Šie du metodai sumažina darbo kiekį, tačiau kartais paieškojus galima rasti ir geresnių metodų.

Rails Engines kūrėjai sumąstė komponentizuoti . Suinstaliavus engines įskiepį atsiranda galimybė kituose įskiepiuose (kurie slepiasi po vendor/plugins katalogu) iškelti kodo dalis. Šia galimybe mes ir pasinaudosime iki galo išnaudodami DRY principą. Tiesa prieš tai teks truputį pagalvoti…

Įskiepis - tai ne tas pats, kas skiepai…

Pradžiai apie pačius įskiepius. Paprasto įskiepio struktūra yra tokia:

  • vendor/
    • plugins/
      • my_plugin/
        • init.rb - failas, kuris įvykdomas startuojant Rails karkasui.
        • install.rb - failas, kuris įvykdomas instaliuojant įskiepį su script/plugin.
        • lib/ - katalogas, kurį Rails įtraukia į užkrovimo kelią (angl. - load path).
          Visi failai ęsantys čia automatiškai tampa pasiekiami jūsų aplikacijai.

          • file1.rb - vienas iš tokių įskiepio failų.

Naudojantis įskiepiais galima suprogramuoti daug įdomių dalykų - pavyzdžiui modelio išplėtimus, filtrus kontroleriams ar papildomus “helper” metodus.

Įdiegus engines įskiepį šis sąrašas prasiplečia. Katalogų struktūra pasipildo šiais katalogais:

  • vendor/
    • plugins/
      • my_plugin/
        • app/
          • controllers/
          • models/
          • views/
        • db/
          • migrate/
        • public/

Argi nieko neprimena? :) Būtent! struktūra. Trumpai apie katalogus:

  • app/ - kaip matote čia tas pats, kas guli po RAILS_ROOT/app/. Visi kontroleriai, modeliai ir vaizdai (angl. views) tampa automatiškai pasiekiami jūsų aplikacijai.
  • db/migrate/ - varikliukai irgi leidžia rašyti migracijas, kurios po to lygiai taip pat sėkmingai gali būti vykdomos su rake db:migrate.
  • public/ - kaip bebūtų gaila, bet ne visuomet komponentai apsiriboja kodu ir išvedamu HTML. Kartais jiems reikia ir vieno ar kito javascript kodo gabaliuko, o galbūt net ir paveiksliuko ar flash’o. Taigi viskas, kas guli šiame kataloge Rails užsikrovimo metu bus perkopijuota į RAILS_ROOT/public/plugin_assets/my_plugin/ katalogą. Tiesa, šis katalogas jau turi būti sukurtas, kitaip nieks nieko nekur nekopijuos.

Verta žinoti!

  • Visi failai, kurie yra RAILS_ROOT/app kataloge turi viršenybę įskiepių atžvilgiu. Tai leidžia jums pakeisti kontrolerius, modelius ar nepatikusius vaizdus savais.
  • Reikia nepamiršti, jog Ruby klasės yra atviros, tad jums niekas nesutrukdys, tarkim, išplėsti įskiepio modelių:

    # RAILS_ROOT/app/models/user.rb
    require File.join(RAILS_ROOT, 'vendor', 'plugins', 'user_engine', 'app', 'models', 'user.rb')
    class User < ActiveRecord::Base
    	after_save :do_interesting_things
    	private
    	def do_interesting_things
    		...
    	end
    end

    Vienintelis minusas, jog kažkodėl pakeitimai RAILS_ROOT/app/models/user.rb ar RAILS_ROOT/vendor/plugins/user_engine/app/models/user.rb failuose nėra iš karto užkraunami development režime; tenka perkraudinėti serverį rankomis: (Kai tik išsiaiškinsiu kodėl - pranešiu :) (greičiausiai web serverio klasių perkraudinėjimo vingrybės).

Kas mums iš to?

Ogi turėdami engines įskiepį ir šiek tiek proto galvoje galime suprojektuoti ir parašyti savo sistemą taip, jog daugiau niekada nebereikėtų perrašinėti kodo dalių - jos jau bus parašytos ir galėsim jas naudoti tiesiog numesdami jas į įskiepių katalogą! Argi ne malonu? :) Reikia forumo? Copy/paste jį į vendor/plugins katalogą, numigruojam duombazę, perkraunam web serverį ir
viskas jau veikia!

Hipotetinė situacija

Tarkime turime tipinį portalą - vartotojai sistemoje, foto galerija, forumas, vartotojų profiliai. Pamąstykime, kokius sąryšius turi kiekviena iš šių dalių su likusia sistema, kokias funkcijas ji atlieka ir kaip mes galime tas funkcijas suskirstyti į varikliukus:

  • Vartotojų varikliukas - leidžia vartotojams užsiregistruoti, išsiregistruoti, pasikeisti esminę informaciją (el. paštą, slaptažodį), priminti slaptažodį.
  • Foto galerijos varikliukas - leidžia įkelti foto, keisti jų dydį, komentuoti jas, kurti katalogus ir pakatalogius.
  • Forumas - leidžia kurti kategorizuotas temas ir atsakinėti į jas, moderuoti forumą.
  • Vartotojų profilių varikliukas - šis truputį įdomesnis. Kodėl jo neprijungus prie vartotojų varikliuko? Galima padaryti ir taip, tačiau:
    • Įsivaizduokime, jog nenorime, jog vartotojai žinotų vienas apie kitą - kam tada tie profiliai?
    • Šis varikliukas gali priklausyti nuo foto galerijos varikliuko (tarkim avatar’ų laikymui), o dėti galeriją į kiekvieną puslapį (kur jos galbūt ir nereikia) yra netikslinga. Nors šitą problemą galima išspręsti pasitelkus patikrinimą ar yra galerijos varikliukas ir atitinkamai įjungiant arba išjungiant avatar’ų funkcionalumą.

Kaip matote, susiduriame su varikliukų priklausomybėmis - tam, kad veiktų vienas, reikalingas kitas. Aišku galima daryti ir nepriklausomus varikliukus (tarkim padaryti failų siuntimą į serverį vartotojų profilių varikliuke), tačiau mūsų tikslas gi buvo kuo labiau sumažinti kodo kartojimą.

(Mano) tipinė varikliuko struktūra

Apsimeskime, jog rašysime vartotojų profilių varikliuką. Pavadinsim jį user_profiles_engine.
Jo failų struktūra:

  • vendor/
    • plugins/
      • user_profiles_engine/
        • app/
          • controllers/
          • models/
          • views/
        • db/
          • migrate/
            • 001_initialize_engine_migration.rb - standartinė ActiveRecord::Migration klasė, kuri aprašo duombazės migraciją.
        • lib/
          • user_profiles_engine.rb - failas laikantis ruby modulį, kurį aš labai mėgstu naudoti įskiepio konfigūracijai.

Jeigu manote, kad čia bus daug kodo - klystate

Prasideda bjauriausia dalis - kodo rašymas. Tiksliau galvojimas, kaip tą kodą parašyti.

Pamėginkime atspėti potencialius grėblius ant kurių galime užlipti berašant šį šedevrą:

Integracija tarp modulių

Pirma mintis šaunanti į galvą yra “o kaip gi ten su ta integracija?”. Vienas iš variantų būtu toks: tarkim mums reikia parodyti vartotojo nuotrauką jo profilyje. Vaizdo kodo ištrauka:

<% if gallery_support? %>
  <%= image_from_gallery @user_profile.photo %>
<% end %>

Ganėtinai paprasta? Pažiūrėkime kas už to slypi:

gallery_support?

Pagalbinis metodas patikrinti ar yra galerijos palaikymas:

# RAILS_ROOT/vendor/plugins/user_profiles_engine/app/helpers/profile_helper.rb
module ProfileHelper
  def gallery_support?
    UserProfilesEngine.gallery_support?
  end
end
# RAILS_ROOT/vendor/plugins/user_profiles_engine/lib/user_profiles_engine.rb
module UserProfilesEngine
  def self.gallery_support?
    (GalleryEngine) ? true : false
  rescue NameError
    false
  end
end

Kaip matote pagalbinis metodas tiesiog persiunčia užklausą į (tą konfiguracinį) modulį, o jis pabando ją pasiekti. Gaila, tačiau negalime naudotis tiesiog defined? GalleryEngine, nes tada autoloader’is nebando užkrauti klasės, o tiesiog patikrinama ar ji jau užkrauta. Gali susidaryti situacija, jog galerijos varikliukas yra, tačiau jis dar neužkrautas, o šis varikliukas pagalvotų, jog jo nėra.

Kodėl reikia persiųsti užklausą į UserProfilesEngine? Tiesiog tas metodas ten logiškiau atrodo (bendruoju atveju).

image_from_gallery

Pagalbinis metodas parodyti paveiksliuką iš galerijos. Iš kur jis atsiranda? Ogi štai:

# RAILS_ROOT/vendor/plugins/gallery_engine/lib/gallery_engine/helpers/application_helper.rb
module GalleryEngine
  module Helpers
    module ApplicationHelper
      def image_from_gallery(photo)
        # Gaunam foto objektą, jeigu mums davė id.
        photo = Photo.find(photo) if photo.is_a? Fixnum
 
        # Darom magiją...
      end
    end
  end
end
# RAILS_ROOT/vendor/plugins/gallery_engine/init.rb
ActionView::Base.send :include, GalleryEngine::Helpers::ApplicationHelper

Kaip matote čia mums padeda Ruby dinamiškumas - galerijos varikliuko užkrovimo metu mes į ActionView::Base įmaišome savo modulį. Naudojames send metodu, jog prieitume prie privataus metodo include.

@user_profile.photo

Šis atributas irgi neatsiranda iš niekur. Turime aprašyti belongs_to (šiuo atveju neskamba labai logiškai, tačiau taip jau reikia) sąryšį:

# RAILS_ROOT/vendor/plugins/user_profiles_engine/app/models/user_profile.rb
class UserProfile < ActiveRecord::Base
  if UserProfilesEngine.gallery_support?
    belongs_to :photo
  end
end

Dabar jau turbūt akivaizdžiai matosi, kodėl mes perkėlėme gallery_support? metodą į UserProfilesEngine modulį? :)

Leidimų valdymas

Kitas grėblys - leidimų valdymas. 99,9% aplikacijų reikia valdyti vartotojus, jog jie nelystų ten, kur jiems nepriklauso. Aš asmeniškai nemėgstu, jog toks kodas būtų integruotas į varikliuką (tai reikštų, jog apribojame jo laisvę). Tad sumąsčiau tokią schemą:

  1. Vartotojas kreipiasi į kontrolerį.
  2. Kontroleryje iškviečiamas before_filter.
  3. before_filter leidžia arba atmeta vartotoją.

Na čia nieko naujo nesumąstyta. Kaip atrodytų tas before_filter?

# RAILS_ROOT/vendor/plugins/user_profiles_engine/app/controllers/profiles_controller.rb
class ProfilesController < ActionController::Base
  before_filter :can_view?, :only => [:index, :view]
  before_filter :can_edit?, :only => [:edit, :delete]
 
  def can_view?
    self.class.can_view?(self)
  end
 
  def can_edit?
    self.class.can_edit?(self)
  end
 
  def self.can_view?(controller=nil)
    true
  end
 
  def self.can_edit?(controller=nil)
    true
  end
end

Kam tas filtrų perkėlimas į klasės metodus? Tai praverčia, kai norime patikrinti tam tikrą salygą (pvz., ar vartotojas gali redaguoti savo profilį) iš kito kontrolerio. Tam, kad nereikėtų kelis kartus rašyti to paties kodo, iškeliame jį į statinius metodus, kuriuos galime pasiekti iš bet kur.

Kokia nauda iš šių metodų? Kol kas jokios. Tačiau nepamirškime, jog Ruby dinamiška kalba. Niekas mums netrukdo padaryti šitaip:

# RAILS_ROOT/app/controllers/profiles_controller.rb
require File.join(RAILS_ROOT, 'vendor', 'plugins', 'user_profiles_engine',
  'app', 'controllers', 'profiles_controller.rb')
class ProfilesController < ActionController::Base
  def self.can_view?(controller=nil)
    # Mano sudėtinga patikrinimo logika.
  end
end

Kadangi failai esantys RAILS_ROOT/app turi viršenybę prieš tuos, kurie pateikiami įskiepių, tai mes sėkmingai perrašysim metodą taip prijungdami savo patikrinimo logiką.

Pačiai patikrinimo logikai jau galite susikurti atskirą varikliuką, o tai kaip ir nebe šio straipsnio tema.

Visi varikliukai - vienišiai

Kuo varikliukas skiriasi nuo turinio valdymo sistemos (TVS) modulio? Lyg ir daro tą patį - pateikia papildomą funkcionalumą ęsamai sistemai. Tačiau yra vienas esminis skirtumas. TVS modulis žino, jog jis turės priėjimą prie tokių ar anokių TVS funkcijų. Viena vertus - gerai, lengviau gyventi programuotojui. Kita vertus - modulis tinka tik vienam TVS.

Na, o varikliukai - vienišiai. Geras varikliukas yra parašytas taip, jog jam reikia minimaliai priklausomybių. Aišku, vartotojų profilių varikliukas neveiks be vartotojų varikliuko, tačiau galerijos varikliukas be pastarojo turi visai smagiai suktis. Sukūrėme naują Rails aplikaciją, įmetėme ir viskas. Veikia.

Kaip tai pasiekti? Reikia truputį pakeisti savo mąstymą. Pavyzdžiui, imkime žinučių sistemą. Žinutes dažniausiai siuntinėja vartotojai vienas kitam. Ar tai reiškia, jog žinučių varikliukas negalės veikti be vartotojų varikliuko? Ne.

Pašto (žinučių) dėžutė labai sėkmingai gali būti autonomiška klasė, kur žinutės keliauja ne iš vartotojo pas kitą vartototą, tačiau iš vienos dėžutės į kitą. Juk ir realiame gyvenime laiškai ateina į dėžutę, o ne pas konkretų asmenį (nekalbant apie registruotus laiškus). O tą dėžutę jau gali tikrinti kad ir keli vartotojai.

Kokie tokios architektūros pliusai?

  • Žinučių varikliukas tampa autonomiškas.
  • Žinučių varikliuku gali naudotis ne tik vartotojai, tačiau ir kitos klasės. Pavyzdžiui, vartotojų grupė irgi gali turėti savo pašto dėžutę, kuria naudotųsi visi tos grupės nariai.

Kas kam priklauso?

Netgi darant varikliukus nepriklausomus dažniausiai jie vis tiek naudojami su kažkokiais sąryšiais. Ne visuomet tie sąryšiai būna vienodi, bet juos reikia kažkur pasižymėti. Tarkime vartotojui priklauso pašto dežutė ir galerija. Kaip mes galime pasižymėti šį faktą? Vienas iš būdų yra message_box_id ir gallery_id laukai duombazės users lentelėje. Tačiau tai nėra pats geriausias būdas, nes tenka redaguoti lentelę, kuri šiaip jau priklauso varikliukui.

Kitas sprendimas - sukurti įskiepį, kuris tuo rūpintųsi. Pavadinkime jį belongs_to_manager. Štai duombazės lentelė (Rails migracijos forma), kuri rūpinsis mūsų priklausomybėm.

# RAILS_ROOT/vendor/plugins/belongs_to_manager/db/migrate/001_initial_table.rb
class InitialTable < ActiveRecord::Migration
  def self.up
    create_table :belongings do |t|
      # Klasės vardas, kuris kažkam priklauso, tarkim UserProfile
      t.string :class_name, :limit => 64, :null => false
      # Klasė, kuriai priklauso, tarkim User
      t.string :belongs_to, :limit => 64, :null => false
      # User id, kuriai priklauso UserProfile
      t.integer :belongs_to_id, :null => false
      # UserProfile id, kuris priklauso User klasei
      t.integer :belonging_id, :null => false
      # Sąryšio tipas: has_one ar has_many.
      t.string :relationship, :limit => 8, :null => false, :default => 'has_one'
    end
  end
 
  def self.down
    drop_table :belongings
  end
end

Turint tokią lentelę belieka parašyti kodą, kuris sukūrus UserProfile objektą jį pririštų prie User objekto. Na, čia daug galima sugalvoti, pradedant paprasčiausiom Create, Retrieve, Update, Delete operacijom baigiant dinaminiu sąryšių tarp objektų generavimu.

Automatizuojame varikliukų prisijungimą prie sistemos

Kadangi mes esame visiški tinginiai, tai norime, kad tiesiog įmetus varikliuką į aplikaciją jis automatiškai prie jos prisijungtų. Tarkim jeigu turime varikliuką, kuris atsakingas už vartotojų registraciją, tai įdiegus vartotojų profilių varikliuką visi vartotojai turėtų automatiškai gauti po profilį.

Tai padaryti mums padės modelio after_save filtrai ir Rails migracijos. Deja, ši dalis nėra labai maloni ir “Ruby-like”.

Tarkime turime klasę A viename faile. Iš kito failo joje norime iškviesti statinį metodą, kuris kažkaip pakeistų pačią klasę.

# failas1.rb
class A
  @@list = []
 
  def self.change_me
    @@list.push @@list.length
  end
end
# init.rb
A.change_me

Viskas atrodytų gerai. Iš init.rb iškviečiamas A.change_me kodas įvykdomas. Problemos iškyla development režime. Mat jame po kiekvienos užklausos yra perkraunamos klasės. O tai reiškia, kad po pirmo kreipimosi į serverį @@list vėl liks tuščias. Negerai… :(

Toliau matysite mano sugalvotą sprendimą šiam apribojimui apeiti (toli gražu ne tobulą).

Pradėkime nuo naujų vartotojų (t.y. tų, kurie registruosis po profilių varikliuko įdiegimo).

Pradžiai užregistruokime naują Proc objektą UserLoginEngine modulyje.
Tas objektas prijungia naują filtrą ir sukuria vartotojo sąryšį su jo profiliu.

# RAILS_ROOT/vendor/plugins/user_profiles_engine/init.rb
UserLoginEngine.extend_class('User', Proc.new {
  User.after_create UserProfilesEngine::AttachProfileToUser.new
  User.has_one :user_profile
} )

Filtras atrodo labai paprastai: tiesiog sukurkime naują profilį ir jį išsaugokime.

# RAILS_ROOT/vendor/plugins/user_profiles_engine/lib/user_profiles_engine/attach_profile_to_user.rb
module UserProfilesEngine
  class AttachProfileToUser
    def after_create(record)
      up = UserProfile.new
      up.user_id = record.id
      up.save!
    end
  end
end

UserLoginEngine modulyje laikome sąrašą tokių Proc objektų.

# RAILS_ROOT/vendor/plugins/user_login_engine/lib/user_login_engine.rb
module UserLoginEngine
  @@extensions = {}
 
  def self.extensions
    @@extensions
  end
 
  def self.get_extensions_for(class_name)
    @@extensions[class_name] || []
  end
 
  def self.extend_class(class_name, method)
    @@extensions[class_name] ||= []
    @@extensions[class_name].push method
  end
end

O User klasėje kiekvieną kartą pereiname per tą sąrašą ir įvykdome kodą Proc objektuose.

# RAILS_ROOT/vendor/plugins/user_login_engine/app/models/user.rb
class User < ActiveRecord::Base
  UserLoginEngine.get_extensions_for(self.to_s).each { |p| p.call } 
end

Pakankamai šlykštu, tačiau veikia. Labai laukiu geresnių sprendimų.

Taigi kuriant naują User objektą ir išsaugojus jį į duomenų bazę automatiškai bus sukurtas ir vartotojo profilis.

Ką gi daryti su senais vartotojais? Tuo pasirūpina Rails migrations.

# RAILS_ROOT/vendor/plugins/user_profiles_engine/db/migrate/002_create_profiles_for_old_users.rb
class CreateProfilesForOldUsers < ActiveRecord::Migration
  def self.up
    # Surandam vartotojus, kurie dar neturi profilio ir jiems sukuriam profilius.
  end
 
  def self.down
    # Sunaikinam visus profilius.
  end
end

Vietoje komentarų kodą pasirašysite patys. :)

Kaip paleisti tas įskiepių migracijas?

Paprastas migracijas tiesiog paleidžiame rake db:migrate komanda. Šios taipogi taip pasileis, tačiau prieš tai jas reikia suintegruoti į bendras aplikacijos migracijas. Tam padaryti reikia:

  1. Nukopijuoti generatorių iš RAILS_ROOT/vendor/plugins/engines/generators/plugin_migration į RAILS_ROOT/lib/generators/plugin_migration.
  2. Paleisti jį: script/generate plugin_migration.
  3. Naudotis rake db:migrate kaip įprasta.

Dar šis tas svarbaus

Jeigu jau naudositės varikliukais, tai pasirūpinkite, jog juos užkrautų reikalinga tvarka (engines įskiepį visuomet pirmą). Tai nustatyti galite RAILS_ROOT/config/enviroment.rb faile.

Apibendrinimas

Kaip matote straipsnis gan didelis, tad teks padaryti išvadas…

  • Rails Engines leidžia rašyti įskiepius, kurie prijungia kodą.
  • Varikliukai turėtų būti rašomi taip, jog galėtų veikti savarankiškai.
  • Tarp varikliukų galimos priklausomybės, tačiau reikia stengtis palikti jų minimaliai.
  • Teisingai pasirašius varikliuką tiesiog pakaks jį numesti į vendor/plugins/ ir jis pradės veikti.
  • Svarbiausia - investavus šiek tiek laiko vėliau jo galima sutaupyti daug daugiau!

Kils klausimų? Komentuokite.

Panašūs straipsniai


“Portalų architektūra - naudojame dar kartą” komentarų: 19

  1. Eimantas

    Labai smagus straipsnis .) šiaip pridurčiau, kad visi tie varikliai (a.k.a Engines) yra kaip web programos paprogramės. Esu skaitęs apie jas, tačiau naudoti asmeniškai neteko (turbūt su Rails dar neteko tiek programuoti arba įgūdžiai kolkas riboti), tačiau vienintelis dalykas, kuris mane sustabdė nuo šių komponentų naudojimo - abejingumas greičio atžvilgiu.

  2. arturaz

    Na greitis greičiu, bet kadangi dauguma web developeriu serga skalabilitu (kaip dammit’as sake), tai visuomet galima ishscalint, kas railsuose pakankamai paprastai darosi.

    be to apie greiti reikia pradet mastyt tada, kai jau stabdo, preoptimizacija baisiai daug neduoda..

    be to cia visas dinamiskumas production mode viena karta ivyksta, o tarkim su php tai tektu freimworka kiekviena karta kraudinet. Kazhin kas dar greiciau :)

  3. kreoton

    As tai skeptiskai ziurau i Rails… Internete daugybe puikiu frameworku ir taip kurie nereikalauja naujos programavimo kalbos mokymosi. Kad ir turbut dabar populiariausias Zend, kuo jis blogesnis uz Rails? turi ir MVC ir nereikia mokytis papildomos programavimo kalbos.

  4. NePo

    kreoton> leisk tave sukritikuoti :)
    Tu neteisingai užduoti klausimą, o klausimas būtų kuo Zend yra geresnis už Rails(bet kurį kitą tavo pasirinktą framework).
    Aš visai nenoriu sukelti fleimo komentaruose.
    kreoton paimk ir parašyk straipsnį:”Zend privalumai”

  5. arturaz

    kreoton - žinok aš irgi skeptiškai žiūrėjau į Rails ir ruby apskritai. Tačiau pabandžiau… ir dabar nebesiimu nei vieno PHP darbo turbūt ne be reikalo? :)

    Tiesiog kiek bandžiau PHP karkasų, tai neteko dar sutikti tokio produktyvaus ir malonaus darbui kaip Rails.

    O Zend framework’as tai (mano ir kelių kolegų nuomone) net ne karkasas, o tiesiog bibliotekų rinkinys, kur dar pats turi laidukus suraišioti, jog viskas normaliai veiktų…

  6. feAR`

    O kaip Cake PHP? :)

  7. arturaz

    O ką tas cakephp? Pabandžiau, tai vos ne vos pabaigiau projektą su juo. Šlykštus dalykas.

    Padielka tokia, kaip kinietiški adadas kedai :)

  8. arturaz

    Čia toks OT, bet kur nunyko gairė konkursas? Nelabai gražu…

  9. arturaz

    Dėl konkursas - nevermind.

    Dėl CakePHP - jaučiu pareigą pakomentuoti plačiau. Prieš pusmetį gal paskutinį kartą čiupinėjau, tai labai įdomiai ten buvo. ActiveRecord toks kaip ir pasityčiojimas, tarkim negali jam tiksliai nurodyti kokius relationshipus įtraukti į užklausą, gali tik lygį nurodyti. Jeigu tau reikia tik vartotojų susietų su post’u, o šiaip to pačio lygio relationshipų yra koks 20, tai teks visus ir traukt.

    Po to grąžinami ne objektai, o paprasti masyvai, kas nėra labai patogu, jei nori toliau kažką daryti. Patingėjo vaikai matyt iteratorius parašyti (o gal nemokėjo).

    Ir paprasčiausiai tokio dalyko kaip engines su PHP šansų nelabai nėra sukurpt. PHP klasės runtime nepamodifikuosi….

  10. asterisk

    arturaz, dėl gairės, tai čia šiaip wordpress šlubuoja.
    Dėl CakePHP, drįsčiau teigti kad dabartinė 1.2 versija yra geresnėje situacijoje. Ir visai nesiskundžiu dirbdabas su juo :) (Kai bandžiau 1.1, keikiau kiek galima, dabar nuomonė daug geresnė).

  11. arturaz

    Na gal ir pasikeitė į gerąją pusę, tačiau tikrai nemanau, jog Rails perspjauna. O su php5 turiu patirties, tad žinau, ką iš jo max galima išlaužt :)

  12. Saulius

    Dėl klasių perkrovimo “development” rėžime, tai verta susipažinti su pluginų pakrovimu čia ir, apskritai, su viso karkaso pakrovimo/veikimo procesu “mongrel” serveryje.
    Atsargiai naudok klasės kintamuosius!!! Paprastai Rails aplikacijos dislokuojamos mongrel klasteriuose, kurie nėra sinchronizuojami tarpusavyje. T.y. sekanti užklausa dažnai bus nukreipiama į kitą mongrel serverį, taigi ankstensnėse užklausose nustatyti klasės kintamieji ( tie su @@ :) nebus pasiekiami. O pats svarbiausias dalykas - Rails’uose naudok “stateless” principa, nes su juo viskas supaprastėja ir išvengsi didelių problemų.
    O, apskritai, viską per daug sudėtinga darai :)

  13. arturaz

    Mielai išgirsčiau kaip visą tai padaryti paprasčiau :)

    Dėl klasės kintamųjų - šiuo atveju tavo pastaba neaktuali, kodas vykdomas inicializacijos metu, tad sinchronizacija nelabai aktuali tampa :)

  14. Saulius

    Dėl klasės kintamųjų, tai čia neminėjau, kad sprendžia konkrečią tavo problemą, tiesiog dalinuosi mūsų patirtimi, nes su jais labai jau lengva prisižaisti, o dažnai jie naudojami ne pagal paskirtį ir ne ten kur reikia. Tavo sprendimas yra hack’as, aš nesuprantu, kodėl tu negali tiesiog require’intis to variklio ten kur reikia taip, kaip daro visas pasaulis. Kitas dalykas - per daug nesusižavėk komponentine paradigma, visose platformose bandoma lipti ant didesnių programinių vienetų, t.y. nuo klasių ant komponentų, bet visiems sekasi ne per geriausiai. Trečia, nesistenk sukurti universalaus daikto, kuris tinka visur ir juo lengva naudotis. Ketvirta, neprogramuok kodo, kurį tu kažkada galėsi lengvai ir patogiai panaudoti, vietoj to, padaryk kelis projektus, ir kai tau užknis kartoti tą patį gabalą, imk jį ir išskirk į pluginą, variklį ar kt. Jei kodas bus padengtas testais maksimaliai, tai išskyrimas (angl. extraction) truks labai trumpai, bet tu tikrai žinosi, kad atskirtas kodas yra tikrai tai ko tau reikia. Patys Rails’ai buvo taip atskirti nuo basecamp’o. Jei nenaudoji Agile metodikų, tai gali ir nesuprasti to.

  15. arturaz

    Na nežinau, Sauliau, galėsime ginčytis po pusmečio kito, kai pavyks (arba ne) tai, ką dabar darau, panaudoti kitur :)

    Dėl to “negaliu requirintis”, tai kur turėčiau requirintis?

    O dėl kodo neprogramavimo, kuriuo kažkada galėsiu patogiai naudotis - aš jau dabar galiu patogiai juo naudotis ir labai sėkmingai kurti ryšius tarp komponentų :) Svarbiausia, kad išmečiau direktoriją, perkroviau serverį ir applikacija pati pasikoregavo ką ji gali ir ko negali daryti :)

  16. Saulius

    Naudok ‘require_dependency’ tose klasėse, kurios yra perkraunamos “development” rėžime. Kitais žodžiais, deklaruok klasę variklio modelių direktorijoje, ten organizuok tą magiją, kuri daro automatinį jos susikonfigūravimą, o ją perkrauk ten, kur prireiks išplėsti funkcionalumą ar jį pakeisti, bet toje vietoje naudok ‘require_dependency’, kad pakrautų pradinę klasę. Taigi, visas grožis pasiliks variklyje, tačiau jei klasę norėsi perkrauti rails pagrindiniuose modeliuose, tai tame modelyje teks įsitraukti perkraunamą klasę pasinaudojant “require_dependcy”.

  17. arturaz

    Bet esmė kokia: yra engine1/app/models/user.rb ir yra engine2/app/models/user.rb. Engine2 praplečia engine1. Engine1 užkraunamas pirmiau, nei engine2, ko pasekoje į load path jo modeliai ateina pirmesni. Tai kaip man nurodyti, jog mongrel perkrovimo metu būtent naudotų user.rb iš engine2, o ne engine1?

    Ar aš kažko nesupratau? :)

  18. Saulius

    Tiosiog pagrindiniame User modelyje (RAILS_ROOT/app/models/user.rb) su ‘require_dependency’ užregistruok sukonstruotą absoliutų path’ą iki antrame plugine esančio user.rb failo.

  19. Rails plugins/engines: Loading files in development mode with "require_dependency"

    […] by Saulius Grigaitis Thu, 22 Nov 2007 20:28:00 GMT The situation we discussed in pixel.lt blog post is interesting and some of you may have such situation. The guy introduced view of reusable […]

Rašyti komentarą

Jūs privalote prisijungti jeigu norite rašyti komentarą.