Cel de-al doilea articol din această serie scurtă vă învață cum să utilizați diverse materiale care vin cu RSpec. Acesta vă arată, de asemenea, cum să tăiați suita de testare prin etichetare, cum funcționează apelurile și cum să extrageți unele date. Ne extindem puțin pe trusa de bază de supraviețuire din primul articol și vă arătăm suficient pentru a fi periculoasă fără prea multă funie pentru a vă închide.
În primul articol am petrecut destul de mult timp încercând să răspundem întrebării "de ce?" De testare. Îți sugerez să ne întoarcem la "cum?" Și să ne păstrăm mai mult context. Am acoperit deja această parte extensivă. Să vedem ce altceva RSpec are de oferit pe care tu, ca începător, îl poți ocupa imediat.
Deci, aceasta va aborda inima lucrurilor. RSPE îți oferă o mulțime de așa-numiții maeștri. Acestea sunt pâinea și untul când vă scrieți așteptările. Până acum ați văzut .pentru a echiv
și .not_to eq
. Dar există un arsenal mult mai mare pentru a vă scrie specificațiile. Aveți posibilitatea să testați pentru a ridica erori, pentru valori de trist și fals, sau chiar pentru anumite clase. Să facem câteva opțiuni pentru a începe:
.pentru a echiv
.not_to eq
Aceasta testează echivalența.
... e 'o descriere inteligentă' Nu așteptați (agent.enemy). La eq 'Ernst Stavro Blofeld' așteptați (agent.enemy) .not_to eq 'Winnie Pooh' sfârșit ...
Pentru a menține lucrurile scurte, am împachetat două declarații așteptate într-una aceasta
bloc. Este o bună practică, totuși, să testați numai un singur lucru pe test. Acest lucru păstrează lucrurile mult mai concentrate, iar testele dvs. vor deveni mai puțin fragile atunci când schimbați lucrurile.
.la be_truthy
.a fi adevarat
... este o descriere inteligentă "nu se așteaptă (agent.hero?) Să se aștepte be_truthy (enemy.megalomaniac?) Să fie adevărat sfârșit ...
Diferența este aceea be_truthy
este adevărat atunci când nu este zero
sau fals
. Deci, va trece dacă rezultatul nu este nici unul dintre aceste două tipuri de "adevărate". .a fi adevarat
pe de altă parte, acceptă doar o valoare care este Adevărat
si nimic altceva.
.pentru a fi_falsy
.a fi falsă
... este o descriere inteligentă "nu te aștepta (agent.coward?) Pentru a fi falsy aștepta (inamic.megalomaniac?) Pentru a fi sfârșitul fals ...
Similar cu cele două exemple de mai sus, .pentru a fi_falsy
se așteaptă fie a fals
sau a zero
valoare, și .a fi falsă
va face doar o comparație directă pe fals
.
.la be_nil
.to_not be_nil
Și nu în ultimul rând, acesta testează exact zero
în sine. Vă ofer un exemplu.
.a se potrivi()
Sper că ați avut deja plăcerea să vă uitați în expresii regulate. Dacă nu, aceasta este o secvență de caractere cu care puteți defini un model pe care îl puneți între două slash-uri pentru a căuta șiruri de caractere. Un regex poate fi foarte util dacă doriți să căutați modele mai largi pe care să le generalizați într-o astfel de expresie.
... este o descriere inteligentă "așteptați (agent.number.to_i). Pentru a se potrivi (/ \ d 3 /) sfârșitul ...
Să presupunem că avem de-a face cu agenți ca James Bond, 007, cărora li se atribuie numere din trei cifre. Apoi am putea încerca pentru asta în mod-primitiv aici, bineînțeles.
>
<
<=
> =
Comparațiile vin la îndemână mai des decât s-ar putea gândi. Presupun că exemplele de mai jos vor acoperi ceea ce trebuie să știți.
... este o descriere inteligentă ... nu ... așteptați (agent.număr) să fie < quartermaster.number expect(agent.number).to be > m.number așteptați (agent.kill_count). să fie> = 25 așteptați (quartermaster.number_of_gadgets). <= 5 end…
Acum ajungem undeva mai puțin plictisitor. De asemenea, puteți să testați pentru clase și tipuri:
.pentru a fi_an_instance_of
.a fi un
.la be_an
... este o descriere inteligentă 'do mission = Mission.create (nume:' Moonraker ') agent = Agent.create (nume:' James Bond ') mission.agents << agent expect(@mission.agents).not_to be_an_instance_of(Agent) expect(@mission.agents).to be_a(ActiveRecord::Associations::CollectionProxy) end…
În exemplul dummy de mai sus, puteți vedea că o listă de agenți care sunt asociate cu o misiune nu sunt de clasă Agent
dar din ActiveRecord :: Asociații :: CollectionProxy
. Ceea ce ar trebui să luați de la acest lucru este că putem testa cu ușurință cursurile în timp ce rămânem foarte expresive. .a fi un
și .la be_an
faceți unul și același lucru. Aveți ambele opțiuni disponibile pentru a păstra lucrurile ușor de citit.
Testarea erorilor este de asemenea convenabilă în RSpec. Dacă sunteți super proaspăt pentru Rails și încă nu sunteți sigur care sunt erorile pe care le poate arunca cadranul, este posibil să nu simțiți nevoia de a le folosi - bineînțeles, ceea ce are sens total. Într-o etapă ulterioară de dezvoltare, le veți găsi foarte utile, totuși. Aveți patru moduri de a trata:
.la raise_error
Acesta este cel mai generic mod. Orice eroare va fi ridicată va fi aruncată în plasă.
.la raise_error (ErrorClass)
În acest fel, puteți specifica exact de la ce clasă să apară eroarea.
.la raise_error (ErrorClass, "Unele mesaje de eroare")
Acest lucru este chiar mai fin granin, deoarece nu menționați doar clasa de eroare, ci un mesaj specific, care ar trebui să fie aruncat cu eroarea.
.la raise_error ("Unele mesaje de eroare)
Sau doar menționați mesajul de eroare fără clasa de erori. Partea așteptată trebuie să fie scrisă puțin diferit, totuși - trebuie să înfășurăm partea de sub text într-un bloc de cod în sine:
... o descriere inteligentă 'do agent = Agent.create (nume:' James Bond ') așteptați agent.lady_killer? Pentru a raise_error (NoMethodError) așteptați double_agent.name .to raise_error (NameError) așteptați double_agent. nume .to raise_error ("Eroare: nu există agenți duble în jurul") așteptați double_agent.name .to raise_error (NameError, "Eroare: Nu există agenți dublu în jurul") sfârșitul ...
.a începe cu
.pentru end_with
Deoarece de multe ori ne ocupăm de colecții atunci când construim aplicații web, este bine să aveți un instrument pentru a le examina. Aici am adăugat doi agenți, Q și James Bond, și am vrut doar să știm cine e primul și ultimul în colecția de agenți pentru o anumită misiune - aici Moonraker.
... este o descriere inteligentă 'do moonraker = Mission.create (nume:' Moonraker ') bond = Agent.create (nume:' James Bond ') q = Agent.create (nume: Q) moonraker.agents << bond moonraker.agents << q expect(moonraker.agents).to start_with(bond) expect(moonraker.agents).to end_with(q) end…
.a include
Acesta este, de asemenea, util pentru a verifica conținutul colecțiilor.
... este o descriere inteligentă 'do mission = Mission.create (nume:' Moonraker ') bond = Agent.create (nume:' James Bond ') mission.agents << bond expect(mission.agents).to include(bond) end…
Acești matrici de predicate sunt o caracteristică a RSpec pentru a crea dinamic matcherii pentru dvs. Dacă aveți metode predicate în modelele dvs., de exemplu (terminând cu un semn de întrebare), atunci RSpec știe că ar trebui să construiască matematici pentru dvs. pe care le puteți folosi în testele dvs. În exemplul de mai jos, dorim să testați dacă un agent este James Bond:
Agent de clasă < ActiveRecord::Base def bond? name == 'James Bond' && number == '007' && gambler == true end… end
Acum, putem folosi acest lucru în specificațiile noastre cum ar fi:
... este o descriere inteligentă 'do agent = Agent.create (nume:' James Bond ', număr:' 007 ', gambler: true) așteptați (agent) .to be_bond final' some clever description 'agent = agent. creați (nume: "James Bond") așteptați (agent) .not_to be_bond sfârșit ...
RSpec ne permite să folosim numele metodei fără semnul întrebării - pentru a forma o sintaxă mai bună, presupun. Răcoros, nu-i așa??
lăsa
și lăsa!
ar putea să arate ca variabile la început, dar ele sunt de fapt metode de ajutor. Primul este evaluat leneș, ceea ce înseamnă că acesta este rulat și evaluat numai când un spec îl folosește de fapt, iar celălalt dat cu bangul (!) Se execută indiferent dacă este folosit de un spec sau nu. Ambele versiuni sunt memorate, iar valorile lor vor fi stocate în cache în același domeniu de aplicare.
descrieți Misiunea, '#prepare',: let let (: misiune) Mission.create (name: 'Moonraker') Let! (: bond) agent.create agenții la o misiune "do mission.prepare (bond) așteptați (mission.agents). să includă sfârșitul obligațiunilor
Versiunea de bang care nu este evaluată leneș poate fi consumatoare de timp și, prin urmare, costisitoare dacă devine noul dvs. prieten fantezist. De ce? Deoarece va configura aceste date pentru fiecare test în cauză, indiferent de ce, și ar putea sfârși în cele din urmă să fie unul dintre aceste lucruri urâte care vă încetinesc suita de test.
Ar trebui să cunoașteți această caracteristică a RSpec de atunci lăsa
este larg cunoscută și utilizată. Acestea fiind spuse, următorul articol vă va arăta câteva probleme cu care ar trebui să știți. Utilizați aceste metode de ajutor cu prudență sau cel puțin în doze mici pentru moment.
RSPE îți oferă posibilitatea de a declara foarte explicit subiectul testat. Există soluții mai bune pentru acest lucru și vom discuta despre dezavantajele acestei abordări în articolul următor, când voi arăta câteva lucruri pe care, în general, doriți să le evitați. Dar pentru moment, să aruncăm o privire la ce subiect
poate face pentru tine:
descrie agentul, "#status" face obiectul Agent.create (nume: 'Bond') 'returnează starea agenților' (subject.status) .not_to 'end'
Această abordare poate, pe de o parte, să vă ajute la reducerea duplicării codurilor, având un protagonist declarat o dată într-un anumit domeniu, dar poate duce și la ceva numit un oaspete mister. Acest lucru înseamnă pur și simplu că s-ar putea să ajungem într-o situație în care folosim datele pentru unul dintre scenariile noastre de testare, dar nu mai avem nicio idee de unde provine de fapt și de la ce se compune. Mai multe despre asta în articolul următor.
În cazul în care nu sunteți conștient de apelurile de apel încă, permiteți-mi să vă dau un brief heads-up. Comenzile de returnare se execută la anumite puncte specifice din ciclul de viață al codului. În ceea ce privește Rails, aceasta ar însemna că aveți un cod care este rulat înainte ca obiectele să fie create, actualizate, distruse etc..
În contextul RSpec, ciclul de viață al testelor este rulat. Asta înseamnă pur și simplu că puteți specifica cârlige care ar trebui executate înainte sau după fiecare test care se execută în fișierul spec, de exemplu, sau pur și simplu în jurul fiecărui test. Există câteva opțiuni cu granulație fină, dar vă recomand să evitați pierderea în detaliu pentru moment. Să începem cu începutul:
înainte de a (: fiecare)
Acest apel de apel este rulat înainte de fiecare exemplar de testare.
descrie agentul "#favorite_gadget" face înainte (: fiecare) face @gagdet = Gadget.create (numele: 'Walther PPK') termină 'returnează un element, gadgetul preferat al agentului' agent = Agent.create : "James Bond") agent.favorite_gadgets << @gadget expect(agent.favorite_gadget).to eq 'Walther PPK' end… end
Să presupunem că veți avea nevoie de un obiect gadget pentru fiecare test pe care îl executați într-un anumit domeniu. inainte de
vă permite să extrageți acest lucru într-un bloc și să pregătiți ușor acest fragment pentru dvs. Când configurați datele în acest fel, trebuie să utilizați, desigur, variabile de instanță pentru a avea acces la acestea în diverse domenii.
Nu vă lăsați păcăliți prin conveniență în acest exemplu. Doar pentru că poți face astfel de lucruri nu înseamnă că ar trebui. Vreau să evit să intru în teritoriul AntiPattern și să vă deranjez dracului, dar, pe de altă parte, vreau să explic puțin dezavantajele acestor exerciții simple de manechin.
În exemplul de mai sus, ar fi mult mai expresiv dacă setați obiectele necesare pe baza testului de testare. În special în cazul fișierelor speculare mai mari, puteți pierde din vedere aceste conexiuni mici și puteți face mai greu pentru alții să pună împreună aceste puzzle-uri.
înainte de (: toate)
Acest inainte de
bloc rulează o singură dată înainte de toate celelalte exemple dintr-un fișier spec.
descrie agentul, '#enemy' face înainte (: toate) do @main_villain = Villain.create (nume: 'Ernst Stavro Blofeld') @mission = Mission.create (nume: 'Moonraker') @ mission.villains << @main_villain end it 'returns the main enemy Bond has to face in his mission' do agent = Agent.create(name: 'James Bond') @mission.agents << agent expect(agent.enemy).to eq 'Ernst Stavro Blofeld' end… end
Când vă amintiți cele patru faze ale testului, inainte de
blocurile uneori sunt utile pentru a stabili ceva pentru tine, care trebuie repetat în mod regulat - probabil lucruri care sunt mult mai meta în natură.
după fiecare)
și dupa toate acestea)
au același comportament, dar sunt pur și simplu rulate după ce testele au fost executate. după
este adesea folosit pentru curățarea fișierelor, de exemplu. Dar cred că este destul de devreme pentru a rezolva problema. Așa că faceți-o în memorie, știți că este acolo în cazul în care începeți să aveți nevoie de ea, și să trecem mai departe pentru a explora alte lucruri de bază.
Toate aceste apelări pot fi plasate strategic pentru a se potrivi nevoilor dumneavoastră. Așezați-le în orice descrie
blocați domeniul de aplicare pe care trebuie să-l executați - acestea nu trebuie neapărat să fie plasate pe partea de sus a fișierului de spec. Ele pot fi ușor de imbricate în interiorul specificațiilor dumneavoastră.
descrie agentul înainte (: fiecare) do @mission = Mission.create (nume: 'Moonraker') @bond = Agent.create (nume: 'James Bond' (: fiecare) nu @main_villain = Villain.create (nume: "Ernst Stavro Blofeld") @ mission.villains << @main_villain end describe 'Double 0 Agent with associated mission' do it 'returns the main enemy the agent has to face in his mission' do @mission.agents << @bond expect(@bond.enemy).to eq 'Ernst Stavro Blofeld' end end describe 'Low-level agent with associated mission' do it 'returns no info about the main villain involved' do some_schmuck = Agent.create(name: 'Some schmuck', number: '1024') @mission.agents << some_schmuck expect(some_schmuck.enemy).to eq 'That's above your paygrade!' end end… end end
După cum puteți observa, puteți plasa blocuri de apel în orice domeniu, după cum doriți, și mergeți la fel de adânc cum aveți nevoie. Codul din apelul de apel va fi executat în cadrul oricărui domeniu de descriere a blocului. Dar un pic de sfat: dacă simțiți nevoia de cuibărit prea mult și lucrurile par să devină puțin dezordonate și complicate, regândiți-vă abordarea și luați în considerare modul în care puteți simplifica testele și configurarea acestora. SĂRUT! Ține-l simplu, prost. De asemenea, acordați atenție cât de frumos se citește atunci când forțează aceste teste să eșueze:
Eșecul Ernst Stavro Blofeld așteaptă: "Ernst Stavro Blofeld" Ernst Stavro Blofeld "Ernst Stavro Blofeld" Ernst Stavro Blofeld "Ernst Stavro Blofeld" Ernst Stavro Blofeld "Ernst Stavro Blofeld "a primit:" Blofeld "2) Agent # inamic Agent de nivel inferior cu misiunea asociată nu întoarce nici o informație despre principalul ticălos implicat Eșec / Eroare: așteptați (some_schmuck.enemy). de așteptat: "Asta e deasupra plății tale!" a primit: "Blofeld"
Să aruncăm o privire rapidă asupra generatoarelor furnizate de RSpec pentru dvs. Ați văzut-o deja când am folosit-o șinele generează rspec: install
. Acest mic tip a făcut să pună RSpec pentru noi rapid și ușor. Ce altceva avem?
rspec: modelul
Doriți să aveți un alt spec?
șinele generează rspec: model another_dummy_model
creează spec / models / another_dummy_model_spec.rb
Rapidă, nu-i așa? Sau un nou spec. Pentru un test de controler, de exemplu:
rspec: controler
șinele generează rspec: controlerul dummy_controller
spec / controlere / dummy_controller_controller_spec.rb
rspec: vizualizare
Același lucru funcționează pentru viziuni, bineînțeles. Nu vom testa însă asemenea opinii. Specificațiile pentru vizualizări vă dau cel mai mic bang pentru buck și este total suficient în probabil aproape orice scenariu pentru a testa indirect opiniile dvs. prin teste de trăsături.
Funcțiile de testare nu sunt o specialitate a RSpec per se și sunt mai potrivite pentru un alt articol. Acestea fiind spuse, dacă sunteți curios, verificați Capybara, care este un instrument excelent pentru astfel de lucruri. Vă permite să testați întregi fluxuri care exercită mai multe părți ale aplicației dvs. - testați caracteristicile complete în timp ce simulați experiența browserului. De exemplu, un utilizator care plătește pentru mai multe articole dintr-un coș de cumpărături.
rspec: ajutor
Aceeași strategie generatoare ne permite să plasăm și un ajutor fără prea multă agitație.
șinele generează rspec: helper dummy_helper
creați spec / helpers / dummy_helper_helper_spec.rb
Dublul helper_helper
partea nu a fost un accident. Când îi dăm un nume mai "semnificativ", veți vedea că RSpec se atașează _ajutor
pe cont propriu.
șinele generează rspec: ajutor important_stuff
creați spec / helpers / important_stuff_helper_spec.rb
Nu, acest director nu este un loc în care să vă îngrijiți metodele ajutoarelor prețioase care apar în timp ce vă refactorizați testele. Acestea ar merge sub spec / suport
, de fapt. spec / ajutoare
este pentru testele pe care ar trebui să le scrieți pentru asistenții dvs. de vedere - un ajutor cum ar fi Seteaza data
ar fi un exemplu comun. Da, acoperirea completă a testelor cu codul dvs. ar trebui să includă și aceste metode de ajutor. Doar pentru că par adesea mici și triviale, nu înseamnă că ar trebui să le ignorăm sau să ignorăm potențialul lor de bug-uri pe care vrem să le prindem. Cu cât este mai complexă ajutorul, cu atât mai mult trebuie să-l scrieți helper_spec
pentru asta!
Doar în cazul în care începeți să jucați cu el imediat, rețineți că trebuie să rulați metodele de ajutor pe o ajutor
obiect atunci când vă scrieți testele de ajutor pentru a lucra. Deci, ele pot fi expuse numai folosind acest obiect. Ceva de genul:
descrie "#set_date" nu ... helper.set_date ... end ...
Puteți utiliza același tip de generatoare pentru specificațiile de caracteristici, specificațiile de integrare și specificațiile de tip mailer. Acestea sunt în afara ariei noastre de azi, dar le puteți angaja în memorie pentru utilizare ulterioară:
Specificațiile pe care le-am creat prin intermediul generatorului de mai sus sunt gata pentru a merge și puteți adăuga testele dvs. acolo imediat. Să aruncăm o privire minusculă asupra unei diferențe între specificații, deși:
cereți "rails_helper" RSpec.describe DummyModel, tip:: modelul face în așteptare "adaugă câteva exemple pentru (sau șterge) # __ FILE__" sfârșit
cereți 'rails_helper' RSpec.describe DummyControllerController, tastați:: controller to end
cereți "rails_helper" RSpec.describe DummyHelperHelper, tastați:: ajutor în așteptare "adăugați câteva exemple la (sau ștergeți) # __ FILE__" sfârșit
Nu are nevoie de un wunderkind pentru a descoperi că toate au diferite tipuri. Acest :tip
Metadatele RSpec vă oferă posibilitatea de a tăia și așezați testele pe structuri de fișiere. Puteți încerca aceste teste un pic mai bine în acest fel. Spuneți că doriți să aveți un fel de ajutoare încărcate doar pentru specificațiile controlerului, de exemplu. Un alt exemplu ar fi că doriți să utilizați o altă structură de directoare pentru specificațiile pe care RSpec nu le așteaptă. Având această metadate în testele dvs., este posibil să continuați să utilizați funcțiile de suport RSpec și să nu călătoriți în suita de testare. Deci, puteți folosi orice structură de directoare pentru dvs. dacă adăugați acest lucru :tip
metadate.
Testele standard RSpec nu depind de metadatele respective, pe de altă parte. Când utilizați aceste generatoare, acestea vor fi adăugate gratuit, dar le puteți evita și ele dacă nu aveți nevoie de ele.
De asemenea, puteți utiliza această metadate pentru filtrarea în specificațiile dvs. Spuneți că aveți un bloc anterior care ar trebui să ruleze numai pe specificațiile modelului, de exemplu. Neat! Pentru suite de testare mai mari, acest lucru ar putea fi foarte util într-o zi. Puteți filtra grupul de teste pe care doriți să-l executați în loc să executați întreaga suită, ceea ce ar putea dura ceva timp.
Opțiunile dvs. se extind dincolo de cele trei opțiuni de etichetare de mai sus, desigur. Să învățăm mai multe despre felierea și testarea testelor în secțiunea următoare.
Când acumulați o suită mai mare de testare de-a lungul timpului, nu va fi suficient doar să rulați teste în anumite foldere pentru a executa teste RSpec rapid și eficient. Ceea ce doriți să puteți face este să executați teste care aparțin împreună, dar ar putea fi răspândite în mai multe directoare. Etichetarea pentru salvare! Nu mă înțelegeți greșit, organizarea testelor dvs. inteligent în dosare este și cheia, dar etichetarea face acest lucru doar un pic mai departe.
Dați testelor dvs. câteva metadate ca simboluri precum: "wip", ": checkout" sau orice se potrivește nevoilor dvs. Când executați aceste grupuri de teste focalizate, pur și simplu specificați că RSpec ar trebui să ignore executarea altor teste de data aceasta prin furnizarea unui pavilion cu numele etichetelor.
descrie agentul,: ștergeți-o "este o mizerie chiar acum" așteptați (agent.favorite_gadgets). la eq 'End Unknown'
rspec - tag wip
Eșecuri: 1) Agentul este un dezastru chiar acum Eșec / Eroare: așteptați (agent.favorite_gadgets). La eq 'Necunoscut' ...
De asemenea, puteți rula tot felul de teste și ignora o grămadă de grupuri care sunt etichetate într-un anumit mod. Trebuie doar să oferiți un tilde (~) în fața numelui etichetei, iar RSpec este fericit să ignore aceste teste.
rspec --tag ~ wip
Rularea mai multe etichete în mod sincron nu este o problemă:
rspec - tag wip - etichetă de control rspec --tag ~ wip - checkout de etichetă
După cum puteți vedea mai sus, puteți să le amestecați și să le potriviți după voia lor. Sintaxa nu este repetată perfectă --etichetă
poate că nu este ideal - dar hei, nu este nici un biggie! Da, toate acestea sunt un pic mai mult de lucru suplimentar și mentale overhead atunci când compuneți specificațiile, dar pe de altă parte, într-adevăr vă oferă o abilitate puternică de a slice suita de testare la cerere. Pe proiecte mai mari, aceasta vă poate economisi o tona de timp în acest fel.
Ceea ce ați învățat până acum trebuie să vă doteze cu elementele de bază absolut necesare pentru a juca cu testele pe cont propriu - un set de supraviețuire pentru începători. Și într-adevăr joacă și fac greșeli cât poți. Luați RSpec și întreaga chestie pe bază de test pentru o rotire și nu vă așteptați să scrieți imediat teste de calitate. Există încă câteva piese care lipsesc înainte de a vă simți confortabil și înainte de a fi eficienți cu aceasta.
Pentru mine, acest lucru a fost un pic frustrant la început, deoarece era greu să vezi cum să testez ceva când nu l-am pus încă în aplicare și nu înțelegeam cum se va comporta.
Testarea într-adevăr dovedește dacă înțelegeți un cadru ca Rails și știți cum se potrivesc piesele împreună. Când scrieți teste, va trebui să puteți scrie așteptări despre cum ar trebui să se comporte un cadru.
Nu este ușor dacă începeți cu toate astea. Confruntarea cu mai multe limbi specifice domeniului - aici RSpec și Rails, de exemplu, plus învățarea API-ului Ruby pot fi confuze ca iad. Nu vă simțiți rău dacă curba de învățare pare a fi descurajantă; acesta va fi mai ușor dacă rămâneți cu el. Efectuarea acestui bec nu se va întâmpla peste noapte, dar pentru mine a meritat foarte mult efortul.