Rake 201

I denne andre artikkelen om Rake dykker vi litt dypere og dekker litt avanserte emner som filoppgaver, regler, multitasks og mer som vil forbedre Rake-koteletter betydelig.

emner

  • Ansvarsfraskrivelse
  • Standard oppgave
  • Oppgaveobjektet
  • Filoppgaver
  • Directory Metode
  • Filverktøy
  • regler
  • Sporflagg
  • Parallelle oppgaver

Ansvarsfraskrivelse

Jeg vil nærme seg dette emnet fra et mer generelt synspunkt. Dette er ikke en artikkel som viser deg en liste over Rake-oppgaver med klare løsninger som er klare til å bli kopiert og limt inn uten mye ettertanke. Det er mer ment å være et blikk under hetten mens du er nybegynner-vennlig og også interessant for folk som nettopp ikke har spilt mye med Rake foruten de åpenbare Rake-oppgaver i Rails. 

Det er forståelsen av verktøyet og hva det tilbyr som gir en høyere avkastning, tror jeg. Jeg håper du ikke bryr deg. For meg er prinsippene mer verdifulle og mer tilnærmet til nybegynnere, og det er opp til deg hva du gjør med det i dine egne applikasjoner.

Standard oppgave

Jeg er sikker på at du i det minste har hørt begrepet tidligere et sted. Men hva er en standardoppgave egentlig? Det er ikke noe magisk, men la oss få dette ut av veien raskt. Når du kjører rake uten noe annet navn for en rakeoppgave, blir standardoppgaven utført.

Shell

rake

Noen Rakefile

desc 'Dette er standardoppgaven. Ingen argumenter nødvendig 'oppgave: standard legger' Noen oppgave som du kanskje vil løpe med jevne mellomrom 'slutt

I Rails skal standard oppgaven kjøre testene dine. Din gjetning er like god som min, men jeg antar at det var et resultat av tester som måtte kjøre oftere enn noen annen oppgave. Når du omdefinerer misligholde Rake oppgave i Rails, det legger bare opp til oppgaven definert av Rails - den vil ikke omdefinere den. Det er faktisk hvordan Rake fungerer. Når du omdefinerer en Rake-oppgave, legger du til de forrige definisjonene.

Filoppgaver

Som navnet antyder, er de oppgaver du utfører på filer (og kataloger). De har et par triks opp i ermene, skjønt. Rake vil selvfølgelig arbeide mye med filene. Ingen overraskelse at noen gjenkjente dette mønsteret og opprettet spesialiserte filoppgaver for din bekvemmelighet - spesielt av den enkle grunnen for å unngå duplisering eller sløsing med behandlingsmuligheter.

Å transformere filer fra en type til en annen er en svært vanlig oppgave. Kildene er dine avhengigheter og oppgavens navn er det som følger fil søkeord. Konvertere Markdown til HTML-filer, konvertere HTML-filer til ebook-formater, JPG-bilder til PNG-bilder, kompilere kildekoden, bygge statiske sider eller bare endre filtillegg og mange flere alternativer står til din disposisjon. Vi kan gjøre alt dette manuelt, men det er selvsagt kjedelig og ineffektivt. Skrivekode for dette er mye mer elegant og skalerbar.

Bruke filoppgaver er ikke mye forskjellig fra "vanlige" oppgaver. De vil også dukke opp hvis du ber om en liste over Rake-oppgaver via rake -T. Faktisk behandler Rake alle oppgaver jevnt, unntatt multitask litt. Å legge til beskrivelser og forutsetninger er ikke noe problem for filoppgaver å håndtere også. 

Faktisk er forutsetninger en nødvendighet for å nevne kildefiler før de blir behandlet. Vi trenger kilden til å eksistere for at dette skal fungere, som selvfølgelig gir mening som en avhengighet. Uten det ville Rake ikke vite hvordan man skal fortsette. Det kan jo ikke skape den nye filen uten tynn luft.

Noen Rakefile

fil 'mi6 / q / gadgets / secret_list.md' => 'mi6 / research / secret_list.md' gjør cp 'mi6 / research / secret_list.md', 'mi6 / q / gadgets / secret_list.md' slutten

Navnet på filoppgaven er i utgangspunktet din målfil, filen du vil ha opprettet. Forutsetningen er kildefilen som trengs for oppgaven. Inne i blokken forteller du Rake hvordan du lager den ønskede utdata-hvordan du bygger den ved hjelp av forutgående fil (er) som allerede eksisterer. Input-output. Dette kan for eksempel være en shell-kommando ved hjelp av pandoc verktøy som forvandler Markdown-filer til HTML-filer. Applikasjonene for filoppgaver er mer enn mange. Syntaxen kan imidlertid føles litt rar i begynnelsen. jeg forstår.

Rake kontrollerer først om målfilen eksisterer og i så fall kontrollerer om tidsstempel er eldre enn forutsetningen filer - en tidsbasert avhengighet. Rake vil kjøre filoppgaven hvis tidsstempelet er eldre enn forutsetningene, eller hvis filen ikke eksisterer ennå. Det er veldig praktisk hvis du trenger å håndtere mer enn et par filer, noe som er spesielt kult fordi du ikke trenger å gjenoppbygge massevis av filer bare fordi du forandret en enkelt i en samling, for eksempel. I motsetning til det, kjøres regelmessige Rake-oppgaver alltid - de sjekker ikke tidsstempler eller andre endringer, med mindre du gjør dem så, selvfølgelig.

Filverktøy

Noen Rakefile

desc 'Endre noen filtype' file 'some_file.new_extension' => 'some_file.old_extension' gjør mv 'some_file.old_extension', 'some_file.new_extension' slutten

Shell

$ rake some_file.new_extension => mv some_file.old_extension some_file.new_extension

I tilfelle du lurer på om cp metode i det forrige eksempelet eller ovenfor mv kommando, la oss snakke om filverktøy. Vi kunne ha brukt sh mv ... å utføre en Shell-kommando fra en Rake-oppgave. Heldigvis for oss, kan vi bruke en modul som gjør Shell kommando ting som denne mye mindre verbose og plattform uavhengig. FileUtils er en Ruby-modul med mange unixy-kommandoer for filoperasjoner: 

  • rm 
  • cp 
  • mv
  • mkdir
  • og så videre…

Hvis reinventing hjulet ikke er din ting, vil FileUtils være en nyttig følgesvenn som håndterer filer. Ofte Rake er alt du trenger, men hver gang i et øyeblikk, vil du være veldig glad denne praktiske modulen har fått ryggen din. RakeUtils utvidet denne modulen litt for enkelhets skyld. 

La oss se på en liste over hva som er til din disposisjon, og så zoom inn på noen få spesielle som kan være av interesse for deg:

cd (dir, alternativer) cd (dir, opsjoner) mkdir_p (dir, opsjoner) mkdir_p (liste, alternativer) rmdir (dir, alternativer) ) rmdir (liste, alternativer) ln (gamle, nye, alternativer) ln (liste, destdir, alternativer) ln_s (gamle, nye, alternativer) ln_s (liste, destdir, alternativer) ln_sf (src, dest, alternativer) cp , dest, alternativer) cp (liste, dir, alternativer) cp_r (src, dest, alternativer) cp_r (liste, dir, alternativer) mv (src, dest, alternativer) mv (liste, dir, alternativer) rm ) rm_r (liste, alternativer) rm_rf (liste, alternativer) installere (src, dest, modus = , alternativer) chmod (modus, liste, alternativer) chmod_R (modus, liste, alternativer) chown (bruker, gruppe, liste, alternativer) chown_R (bruker, gruppe, liste, alternativer)

Selv om jeg antar at du er nybegynner, antar jeg også at du har spilt med Rails før, og at du kjenner de grunnleggende Unix-verktøyene-ting som mv, cd, pwd, mkdir og ting. Hvis ikke, gjør leksene dine og kom tilbake. 

I Rakefiles kan du bruke disse metodene rett ut av boksen. Og for å unngå misforståelser, er dette et Ruby-lag som "imiterer" disse Unix-kommandoene, og som du kan bruke i Rakefiles uten noen prefikser som sh-for å utføre en Shell-kommando. Forresten, den opsjoner du ser på listen ovenfor betyr en hash av alternativer. La oss se på noen få interessante kommandoer som kan komme til nytte ved å skrive filoppgaver:

  • sh

Dette lar deg utføre shell kommandoer fra din Ruby-filer.

  • cd

Dette er en veldig grunnleggende, men det er noe kult om denne kommandoen. Hvis du gir cd Med en blokk endrer den gjeldende katalogen til bestemmelsesstedet, gjør virksomheten som definert i blokken, og går deretter tilbake til forrige arbeidsregister for å fortsette. Ryddig, faktisk!

  • cp_r

Lar deg kopiere filer og kataloger rekursivt i bulk.

  • mkdir_p

Oppretter en målkatalog og alle angitte foreldre. Heldigvis for oss har vi katalog metode i Rake, som er enda mer praktisk, og derfor trenger vi ikke det.

  • ta på

Dette oppdaterer tidsstempelet for en fil hvis den eksisterer - hvis ikke, blir den opprettet.

  • identisk?

Lar deg sjekke om to filer er de samme.

Directory Metode

I Rake har du en praktisk måte å definere kataloger uten å bruke mkdir eller mkdir_p. Det er spesielt nyttig når du trenger å bygge opp nestede kataloger. Et mappetre kan være en smerte hvis du trenger å bygge opp en katalogstruktur via flere filoppgaver som har tonnevis av forutsetninger for katalogstrukturen. Tenk på katalog metode som mappeoppgave.

Noen Rakefile

katalog 'mi6 / q / special_gadgets'

Dette skaper de aktuelle katalogene uten mye oppstyr. Det som kanskje ikke er åpenbart med en gang, er at du kan stole på det som enhver annen rakeoppgave - som en forutsetning. Bare vær sikker på at navnet på filoppgaven, navnet sitt, inneholder katalogen du stoler på. Hvis flere oppgaver er avhengige av det, vil det fortsatt bli opprettet bare én gang.

katalogen 'mi6 / q / gadgets' desc 'Overfør hemmelig forsknings gadgets' fil 'mi6 / q / gadgets / gadget_list.md' => 'mi6 / q / gadgets' gjør cp 'gadget_list.md', 'mi6 / q / special_gadgets /secret_gadget_list.md 'slutten

Som du kan se her, er Rake veldig konsistent og tenker på alle tingene som skal bygges som oppgaver. Takk, Jim, som gjør livet enkelt!

regler

Regler kan hjelpe oss med å redusere duplikasjoner når vi håndterer oppgaver-filoppgaver, faktisk. I stedet for å instruere Rake å utføre oppgaver på bestemte filer som somefile.markdown, Vi kan lære Rake å utføre disse oppgavene på en bestemt type fil, som et mønster eller en blåkopi. Å transformere et sett med filer i stedet for enkle er en mye mer allsidig og tørr tilnærming. Oppgaver som disse skaleres mye bedre når vi definerer et mønster for filer som deler lignende egenskaper.

Noen Rakefile

fil "quartermaster_gadgets.html" => "quartermaster_gadgets.markdown" gjør sh "pandoc-s quartermaster_gadgets.markdown -o quartermaster_gadgets.html" slutten

Som du kan se, å ha en masse filer ville være kjedelig å opprettholde den måten. Ja, vi kan skrive vårt eget skript der vi holder en liste over filer i en matrise og lukter over det, men vi kan gjøre det bedre, mye bedre. 

En annen uønsket bivirkning ville være at hver gang vi kjører et slikt skript, blir alle HTML-filer gjenoppbygd - selv om de ikke har endret seg i det hele tatt. En stor liste med filer vil få deg til å vente mye lenger eller ta opp mye mer ressurser enn nødvendig. Vi trenger ingen ekstra kode for å ta vare på flere filer. Rake gjør en bedre og mer effektiv jobb i den avdelingen siden den bare utfører sine filoppgaver eller regler når filen ble rørt i mellomtiden.

Noen Rakefile

regelen ".html" => ".markdown" do | rule | sh "pandoc-s # rule.source -o # rule.name" slutten

Når vi definerer en regel som ovenfor, har vi en mekanisme på plass for å transformere alle filer med en .Markdown utvidelse til en .html fil. Med regler er Rake først ute etter en oppgave for en bestemt fil som quartermaster_gadgets.html. Men når den ikke finner en, bruker den sletten .html Regel å se etter en kilde som kunne oppnå vellykket utførelse. På den måten trenger du ikke å lage en lang liste med filer, men bare bruk en generell "regel" som definerer hvordan du håndterer bestemte filoppgaver. Ganske utrolig!

Oppgaveobjekt

I regelen ovenfor brukte vi oppgaveobjektet - i dette tilfellet et regelobjekt, for å være enda mer presist. Vi kan sende det som et blokkargument inn i nedleggelsen og anropsmetoder på den. Som med filoppgaver, handler det om oppgavekilder, dets avhengigheter - en markdown-fil, for eksempel - og oppgavens navn. 

Fra reglementets kropp i blokken (og filoppgaver) har vi tilgang til reglenees navn og kilde. Vi kan trekke ut informasjon fra det argumentet som passerte-navnet gjennom rule.name og dens kilde (aka filkilde) via rule.source. Ovenfor kan vi unngå å duplisere navnene på filene og generalisere et mønster i stedet. På samme måte kan vi få listen over forutsetninger eller avhengigheter med rules.prerequisites. For filoppgaver eller andre oppgaver gjelder det selvsagt også.

Snakker av avhengigheter, kan de fungere som en liste for å bli iterert. Det er ikke nødvendig å lage en egen Hver loop hvis du spiller kortene dine rett. 

Oppgave: html =>% W [quartermaster_gadgets.html, research_gadgets.html] regel ".html" => ".md" gjør | r | sh "pandoc-s # r.source -o # r.name" slutten

Som du kan se, behøvde vi ikke å manuelt lytte over listen over artikler. Vi satte ganske enkelt Rake på jobb og brukte avhengighetene - som er mye enklere og renere.

Hva er enda kjøligere for DRYing stuff up er at reglene kan ta en proc objekt - et anonymt funksjon objekt, en lambda i utgangspunktet-som en forutsetning. Det betyr at i stedet for bare et enkelt mønster som en forutsetning, kan vi passere noe mer dynamisk som lar oss kaste et nett av mønstre som fanger mer enn en enkelt fisk. For eksempel, regler for .Markdown og .md filer. 

De ville ha samme kropp av regelen, men bare et annet mønster som forutsetning. Det er som å definere en ny filoppgave for hvert objekt som returneres av proc-objektet. En annen måte å jobbe med regler er selvsagt regelmessige uttrykk. Du sender et mønster som en avhengighet, og hvis du har en kamp, ​​kan filoppgaven utføres. Søte alternativer, nei?

some_markdown_list = [...] detect_source = proc do | html_file_name | some_markdown_list.detect | markdown_source | markdown_source.ext == html_file_name.ext end rule '.html' => detect_source do | r | sh "pandoc-s # r.source -o # r.name" slutten

Forskjellen mellom Procs og Lambdas

Hvis du er ny til lambda land eller ikke har funnet det helt ut, er det her en liten oppdatering. Procs er objekter du kan passere rundt som kan utføres senere, så er lambdas. Begge er Proc gjenstander, forresten. Forskjellen er subtil og kommer ned til argumentene som sendes inn i dem. Lambdas kontrollerer antall argumenter og kan blåse opp med en ArgumentError for den grunn-pross bryr seg ikke. Den andre forskjellen er med hensyn til håndtering av returopplysninger. Procs kommer ut av det omfanget hvor proc-objektet ble utført. Lambdas avslutter bare lambda-omfanget og fortsetter å utløse neste kode som er i tråd, så å si. Ikke super viktig her, men jeg tenkte på nybegynnere blant dere, det kan ikke skade heller.

Nyttige flagg

Dette er en kort liste over flagg som du kan passere for å rake oppgaver.

  • --regler

Viser deg hvordan Rake prøver å bruke regler - et spor for regler. Uvurderlig hvis du håndterer et par regler og løper inn i feil.

Shell

$ rake quartermaster_gadgets.html --rules Forsøk regel quartermaster_gadgets.html => quartermaster_gadgets.md (quartermaster_gadgets.html => quartermaster_gadgets.md ... EXIST) pandoc-s quartermaster_gadgets.md -o quartermaster_gadgets.html
  • -t

Husk solve_bonnie_situation Oppgave fra artikkel ett? La oss legge til dette flagget til denne Rake-oppgaven og slå på sporing. Vi får også en backtrace hvis vi går inn i feil. Dette er absolutt praktisk for feilsøking.

Shell

$ rake solve_bonnie_situation -t ** Invoke solve_bonnie_situation (first_time) ** Invoke get_mr_wolf (first_time) ** Utfør get_mr_wolf Du har ikke noe problem Jules, jeg er på det! Gå inn der og slapp dem ut og vent på ulven som skulle komme direkte! ** Invoke calm_down_jimmy (first_time) ** Utfør calm_down_jimmy Jimmy, gjør meg en tjeneste, vil du? Jeg luktet litt kaffe der tilbake. Ville du lage meg en kopp? ** Invoke figure_out_bonnie_situation (first_time) ** Utfør figure_out_bonnie_situation Hvis jeg ble informert riktig, tikkes klokken. Er det riktig Jimmy? ** Invoke get_vince_vega_in_line (first_time) ** Utfør get_vince_vega_in_line Kom igjen? Få det rett buster. Jeg er ikke her for å si takk! Jeg er her for å fortelle deg hva du skal gjøre! ** Invoke clean_car (first_time) ** Utfør clean_car Jeg trenger deg to fellas for å ta disse rengjøringsprodukter og rengjør innsiden av bilen. Jeg snakker fort, fort, fort! ** Invoke clean_crew (first_time) ** Utfør clean_crew Jim, såpen! O.K. Herrer, dere har begge vært i fylke før jeg er sikker. Her kommer det! ** Invoke get_rid_of_evidence_at_monster_joes (first_time) ** Utfør get_rid_of_evidence_at_monster_joes Så hva er med antrekkene? Går dere til et volleyballspill eller noe? ** Invoke drive_into_the_sunrise (first_time) ** Utfør drive_into_the_sunrise Ring meg Winston! ** Utfør solve_bonnie_situation Du vet, jeg vil gå til frokost. Føl deg som å ha frokost med meg?

Tracing Rules

Innstilling Rake.application.options.trace_rules = true i en Rakefile forteller selv Rake å vise oss spore informasjon om regler når vi driver en oppgave. Dette er kult fordi når vi kjører et spor via rake -t, Med et enkelt flagg får vi all informasjon om feilsøking vi trenger. Vi får ikke bare en liste over oppgaveanrop, men kan også se hvilke regler som ble brukt eller forsøkt.

  • -P

Viser en liste over forutsetninger for alle oppgaver. Her bruker vi igjen solve_bonnie_situation oppgave. Udelatelse av utdata for andre oppgaver, dette ville være utpekt utgang:

Shell

$ rake solve_bonnie_situation -P ... rake solve_bonnie_situation get_mr_wolf calm_down_jimmy figure_out_bonnie_situation get_vince_vega_in_line clean_car clean_crew get_rid_of_evidence_at_monster_joes drive_into_the_sunrise ... 

Hvis du er nysgjerrig, løp rake -P. Ganske interessant utgang.

  • -m

Kjører oppgaver som multitasks.

Parallelle oppgaver

La meg introdusere deg til multitask metode. Dette kan hjelpe deg med å øke hastigheten litt, etter hvert har vi flere kjerner på de fleste moderne datamaskiner, så la oss gjøre bruk av dem. Selvfølgelig kan du alltid oppnå hastighetsøkningene ved å skrive fast kode som mangler noe fett, men løpende oppgaver parallelt kan sikkert gi deg noe ekstra i den forbindelse. Det er imidlertid fallgruver, som vi også vil dekke.

De oppgavene vi har utført hittil, løper alle oppgaver i rekkefølge, den ene etter den andre. Det er en sikker innsats hvis koden din er i orden, men det er også tregere. Hvis hastighet er viktig for en viss oppgave, kan vi hjelpe litt ved multi-threading-oppgaver. Vær imidlertid oppmerksom på at sekvensiell tilnærming er det bedre alternativet under noen omstendigheter.

La oss si at vi har tre Rake-oppgaver som må løpe som en forutsetning for å kunne utføre en fjerde. Disse er fire tråder, i utgangspunktet. I det større bildet av ting, når du kjører flere applikasjoner - eller for å være mer spesifikk, prosesser - samtidig, er den samme ideen på jobb.

multitask: shoot_bond_movie => [: shoot_car_chase,: shoot_love_scene,: shoot_final_confrontation] legger "Hovedfotografering er ferdig, og vi kan begynne å redigere." slutt

Ved hjelp av multitask, Avhengighetene i vår forutsetningsklasse er nå ikke utført i denne rekkefølgen lenger. I stedet er de spredt og løp parallelt - men før shoot_bond_movie oppgave, selvfølgelig. En Ruby-tråd for hver oppgave vil bli kjørt samtidig. Når de er ferdige, shoot_bond_movie vil gjøre sin virksomhet. Måten oppgaver handler her ligner på randomisering, men faktisk blir de rett og slett henrettet samtidig.

Den vanskelige delen er bare for å sikre at visse avhengigheter behandles i en ordre som passer dine behov. På grunn av dette må vi ta vare på løpevilkårene. Dette innebærer i utgangspunktet at en viss oppgave løper inn i problemer fordi ordren med henrettelse hadde utilsiktede bivirkninger. Det er en feil. 

Hvis vi kan unngå det, oppnår vi trådsikkerhet. Med hensyn til vanlige forutsetninger, interessant, vil disse forutsetningene bli kjørt bare en gang fordi multitaskforutsetningene venter på ferdigstillelse først.

oppgave: shoot_love_scene do ... sluttoppgave: prepare_italy_set gjør ... sluttoppgave: shoot_car_chase => [: prepare_italy_set] gjør ... sluttoppgave: shoot_final_confrontation => [: prepare_italy_set] gjør ... slutt multitask: shoot_bond_movie => [: shoot_car_chase,: shoot_love_scene,: shoot_final_confrontation ] setter "Hovedfoto er gjort, og vi kan begynne å redigere." slutt

Både shoot_car_chase og shoot_final_confrontation oppgavene er avhengige av prepare_italy_set å fullføre først-som bare kjøres en gang, forresten. Vi kan bruke denne mekanismen til å forutse rekkefølgen når du kjører oppgaver parallelt. Ikke bare stol på rekkefølgen på utførelse hvis det er noe viktig for oppgaven din.

Siste tanker

Vel, jeg antar at du nå er fullt utstyrt for å skrive noen alvorlige Rake-virksomheter. Å gjøre riktig bruk av dette verktøyet vil forhåpentligvis gjøre livet ditt som en Ruby-utvikler enda mer glad. I denne andre artikkelen håper jeg at jeg kunne formidle hva et enkelt, men fantastisk verktøy Rake egentlig er. Den ble skapt av en ekte mester i hans håndverk.  

Vi skylder alle Jim Weirich enorm respekt for å komme opp med dette elegante byggverktøyet. Ruby-samfunnet er absolutt ikke helt det samme siden han døde. Jims arv er tydeligvis her for å bli, skjønt. En annen gigant vi er privilegert å bygge på.