I den här andra artikeln om Rake dykar vi lite djupare och täcker lite avancerade ämnen som filuppgifter, regler, multitasks och mer som kommer att förbättra dina Rake-kotletter betydligt.
Jag vill närma mig detta ämne ur en mer allmän synvinkel. Det här är inte en artikel som visar en lista över Rake-uppgifter med klara lösningar som är klara att kopiera och klistra in utan stor eftertanke. Det är mer tänkt att vara en titt under huven samtidigt som den är nybörjevänlig och också intressant för människor som bara inte har spelat mycket med Rake förutom de uppenbara Rake-uppgifterna i Rails.
Det är förståelsen av verktyget och vad det erbjuder som ger en högre avkastning, tror jag. Jag hoppas att du inte har något emot det. För mig är det mer värdefullt och mer tillvägagångssätt för nybörjare, och det är upp till dig vad du gör med det i dina egna applikationer.
Jag är säker på att du åtminstone hört termen tidigare någonstans. Men vad är en standarduppgift verkligen? Det är inget magi, men låt oss ta det här snabbt. När du kör räfsa
utan ytterligare namn för en rake-uppgift utförs standarduppgiften.
räfsa
desc 'Detta är standarduppgiften. Inga argument behövs "Uppgift: Standard lägger" En del uppgift som du kanske vill springa regelbundet "slut
I Rails är standarduppgiften att köra dina test. Din gissning är lika bra som min, men jag antar att det var ett resultat av test som behöver köras oftare än någon annan uppgift. När du omdefinierar standard
Rake uppgift i Rails, det lägger bara upp till den uppgift som definieras av Rails-den kommer inte att omdefiniera den. Det är faktiskt hur Rake fungerar. När du omdefinierar en Rake-uppgift lägger du till de tidigare definitionerna.
Som namnet antyder är de uppgifter som du utför på filer (och kataloger). De har dock några knep på sina ärmar. Rake kommer självklart att arbeta mycket med tiden. Det är ingen överraskning att någon kände igen det här mönstret och skapat specialiserade filuppgifter för din bekvämlighet, särskilt av enkla skäl att undvika dubbelarbete eller slösa bort bearbetningskapaciteten.
Att transformera filer från en typ till en annan är en mycket vanlig uppgift. Källorna är dina beroenden och uppgiftsnamnen är följande fil
nyckelord. Konvertera Markdown till HTML-filer, konvertera HTML-filer till ebook-format, JPG-bilder till PNG-bilder, kompilera källkod, bygga statiska sidor eller bara ändra filtillägg och många fler alternativ står till ditt förfogande. Vi kan göra allt detta manuellt, men det är naturligtvis tråkigt och ineffektivt. Skriv kod för detta är mycket mer elegant och skalbar.
Att använda filuppgifter är inte mycket annorlunda än "vanliga" uppgifter. De kommer också att dyka upp om du ber om en lista över Rake-uppgifter via rake -T
. I själva verket behandlar Rake alla uppgifter lika - utom multi aktivitet
lite. Att lägga till beskrivningar och förutsättningar är inte ett problem för att filuppgifter ska hanteras också.
Faktum är att förutsättningar är nödvändiga för att nämna källfiler innan de behandlas. Vi behöver källan att existera för att detta ska fungera - vilket är självklart som ett beroende. Utan det skulle Rake inte veta hur man fortsätter. Det kan trots allt inte skapa den nya filen ur tunna luften.
fil 'mi6 / q / gadgets / secret_list.md' => 'mi6 / research / secret_list.md' gör cp 'mi6 / research / secret_list.md', 'mi6 / q / gadgets / secret_list.md' slutet
Namnet på din filuppgift är i grunden din målfil, filen du vill ha skapat. Förutsättningen är källfilen som behövs för uppgiften. Inne i blocket berättar du Rake hur man skapar önskad utdata-hur man bygger den med de förutsättningar som redan finns. Input-output. Det kan till exempel vara ett skalkommando med hjälp av pandoc
verktyg som förvandlar Markdown-filer till HTML-filer. Applikationerna för filuppgifter är mer än mycket. Syntaxen kan dock känna sig lite konstig först. jag fattar.
Rake kontrollerar först om målfilen existerar och, om så är fallet, kontrollerar om tidsstämpeln är äldre än förhandsfilerna - ett tidsbaserat beroende. Rake kommer att köra filuppgiften om tidsstämpeln är äldre än förutsättningarna eller om filen inte existerar än. Det är väldigt användbart om du behöver hantera mer än ett par filer, vilket är särskilt coolt eftersom du inte behöver bygga upp en massa filer bara för att du ändrat en enda i en samling, till exempel. I kontrast till det är regelbundna Rake-uppgifter alltid körda - de kontrollerar inte några tidstämplar eller andra ändringar, såvida du inte gör dem så, förstås.
desc 'Ändra filtilläggsfil' some_file.new_extension '=>' some_file.old_extension 'gör mv' some_file.old_extension ',' some_file.new_extension 'slut
$ rake some_file.new_extension => mv some_file.old_extension some_file.new_extension
Om du undrar om cp
metod i föregående exempel eller ovan mv
kommando, låt oss prata om filverktyg. Vi kunde ha använt sh mv ...
att utföra ett Shell-kommando från en Rake-uppgift. Lyckligtvis för oss kan vi använda en modul som gör Shell kommandot saker som denna mycket mindre verbose och plattform oberoende. fileutils
är en Ruby-modul med många unixy-kommandon för filoperationer:
rm
cp
mv
mkdir
Om du inte uppfinnar hjulet igen är FileUtils en användbar kompanjon som hanterar filer. Ofta är Rake allt du behöver, men varje gång i taget blir du väldigt glad att den här praktiska modulen har fått dig tillbaka. RakeUtils
förlängde denna modul något för din bekvämlighet.
Låt oss ta en titt på en lista över vad som står till ditt förfogande och sedan zooma in på några speciella som kan vara av intresse för dig:
cd (dir, alternativ) cd (dir, alternativ) cd (dir, alternativ) mwdir (dir, options) mkdir (dir, options) ) rmdir (lista, alternativ, alternativ) ln (gamla, nya, alternativ) ln (lista, destdir, alternativ) ln_s (gamla, nya, alternativ) ln_s (lista, destdir, alternativ) ln_sf (src, dest, alternativ) cp , dest, alternativ) cp (lista, dir, alternativ) cp_r (src, dest, alternativ) cp_r (lista, dir, alternativ) mv (src, dest, alternativ) mv (lista, dir, alternativ) rm ) rm_r (lista, alternativ) rm_rf (lista, alternativ) installera (src, dest, mode =, chmod (alternativ, lista, alternativ) chmod (användare, grupp, lista, alternativ) chown_R (användare, grupp, lista, alternativ) touch (lista, alternativ)
Även om jag antar att du är nybörjare, antar jag också att du har spelat med Rails tidigare och att du känner till de grundläggande Unix-verktygen-saker som mv
, CD
, pwd
, mkdir
och sånt. Om inte, gör dina läxor och kom tillbaka.
I dina Rakefiles kan du använda dessa metoder direkt ur rutan. Och för att undvika missförstånd är detta ett Ruby-lager som "imiterar" dessa Unix-kommandon och som du kan använda i dina Rakefiles utan några prefix som sh
-för att utföra ett Shell-kommando. Förresten, den alternativ
du ser i listan ovan betyder en hash av alternativen. Låt oss titta på några intressanta kommandon som kan komma till nytta för att skriva filuppgifter:
sh
Detta låter dig utföra skalkommandon från dina Ruby-filer.
CD
Det här är en mycket grundläggande, men det finns något coolt om det här kommandot. Om du tillhandahåller CD
Med ett block ändras den aktuella katalogen till dess destination, gör verksamheten som definierad i blocket och återgår sedan till föregående arbetsmapp för att fortsätta. Snyggt, faktiskt!
cp_r
Låter dig kopiera filer och kataloger rekursivt i bulk.
mkdir_p
Skapar en målkatalog och alla angivna föräldrar. Lyckligtvis för oss har vi katalog
metod i Rake, vilket är ännu mer bekvämt, och därför behöver vi inte det.
Rör
Detta uppdaterar tidsstämpeln för en fil om den existerar - om inte, skapas den.
identisk?
Låt dig kontrollera om två filer är desamma.
I Rake har du ett praktiskt sätt att definiera kataloger utan att använda mkdir
eller mkdir_p
. Det är särskilt användbart när du behöver bygga upp kapslade kataloger. Ett mappträ kan vara en smärta om du behöver bygga upp en katalogstruktur via flera filuppgifter som har många förutsättningar för katalogstrukturen. Tänk på katalog
Metod som en mappuppgift.
katalog 'mi6 / q / special_gadgets'
Detta skapar katalogerna i fråga utan mycket krångel. Det som inte kan vara uppenbart direkt är att du kan lita på det som någon annan rakeuppgift - som en förutsättning. Se bara till att namnet på filuppgiften, dess namn, innehåller den katalog du är beroende av. Om flera uppgifter beror på det kommer det fortfarande att skapas enbart en gång.
katalog 'mi6 / q / gadgets' desc 'Överför hemlig forsknings gadgets' fil 'mi6 / q / gadgets / gadget_list.md' => 'mi6 / q / gadgets' gör cp 'gadget_list.md', 'mi6 / q / special_gadgets /secret_gadget_list.md 'slutet
Som du kan se här är Rake mycket konsekvent och tänker på alla saker som ska byggas som uppgifter. Tack, Jim, det gör livet enkelt!
Regler kan hjälpa oss att minska dubbletter när vi hanterar uppgifter-filuppgifter, faktiskt. I stället för att instruera Rake att utföra uppgifter på specifika filer som somefile.markdown
, vi kan lära Rake att utföra dessa uppgifter på en viss typ av fil, som ett mönster eller en blueprint. Att omvandla en uppsättning filer i stället för singlar är ett mycket mer mångsidigt och torrt sätt. Uppgifter som dessa skala mycket bättre när vi definierar ett mönster för filer som delar liknande egenskaper.
filen "quartermaster_gadgets.html" => "quartermaster_gadgets.markdown" slutar sh "pandoc -s quartermaster_gadgets.markdown -o quartermaster_gadgets.html"
Som du kan se skulle det vara tråkigt att ha en massa filer för att behålla det sättet. Ja, vi kan skriva vårt eget skript där vi håller en lista över filer i en array och iterera över det, men vi kan göra bättre mycket bättre.
En annan oönskad bieffekt skulle vara att varje gång vi kör ett sådant skript, blir alla HTML-filer ombyggda - även om de inte alls har ändrats. En stor lista med filer skulle få dig att vänta mycket längre eller ta upp mycket mer resurser än nödvändigt. Vi behöver ingen extra kod för att ta hand om flera filer. Rake gör ett bättre och mer effektivt jobb i den avdelningen eftersom det bara utför sina filuppgifter eller regler när filen berördes under tiden.
regel ".html" => ".markdown" gör | regel | sh "pandoc-s # rule.source -o # rule.name" slut
När vi definierar en regel som ovan har vi en mekanism för att omforma en fil med en .prissänkning
förlängning till en .html
fil. Med regler söker Rake först en uppgift för en viss fil som quartermaster_gadgets.html
. Men när den inte kan hitta en, använder den slätten .html
regel att leta efter en källa som kan uppnå framgångsrikt utförande. På så sätt behöver du inte skapa en lång lista med filer utan bara använda en generell "regel" som definierar hur man hanterar vissa filuppgifter. Ganska häftigt!
I regeln ovan utnyttjade vi uppgiftsobjektet - i det här fallet ett regelobjekt - att vara ännu mer exakt. Vi kan överföra det som ett blockargument till nedläggningen och samtalsmetoderna på den. Precis som med filuppgifter gäller regler om uppgiftskällor, dess beroende - en markdown-fil, till exempel - och dess uppgiftsnamn.
Inom reglernas kropp i kvarteret (och filuppgifter) har vi tillgång till reglerna namn och källa. Vi kan extrahera information från det argumentet passerat-namnet igenom rule.name
och dess källa (aka filkälla) via rule.source
. Ovan kan vi undvika att duplicera namnen på filerna och generalisera ett mönster istället. På samma sätt kan vi få listan över förutsättningar eller beroenden med rules.prerequisites
. För filuppgifter eller annan uppgift gäller det självklart.
Tala om beroenden, de kan fungera som en lista som ska repeteras. Det finns ingen anledning att skapa en separat varje
loop om du spelar dina kort rätt.
uppgift: html =>% W [quartermaster_gadgets.html, research_gadgets.html] regel ".html" => ".md" gör | r | sh "pandoc-s # r.source -o # r.name" slut
Som du kan se behövde vi inte manuellt iterera över listan över artiklar. Vi lade helt enkelt Rake till jobbet och använde beroenden - vilket är mycket enklare och renare.
Vad som är ännu svalare för DRYing stuff up är att regler kan ta ett proc objekt - ett anonymt funktionsobjekt, en lambda i grunden-som en förutsättning. Det betyder att istället för endast ett enda mönster som en förutsättning kan vi passera något mer dynamiskt som låter oss kasta ett nät av mönster som fångar mer än en enda fisk. Till exempel regler för .prissänkning
och .md
filer.
De skulle ha samma regelregel men endast ett annat mönster som förutsättning. Det är som att definiera en ny filuppgift för varje objekt som returneras av proc-objektet. Ett annat sätt att arbeta med regler är naturligtvis regelbundna uttryck. Du skickar ett mönster som ett beroende och om du har en match kan filuppgiften utföras. Söta alternativ, nr?
some_markdown_list = [...] detect_source = proc do | html_file_name | some_markdown_list.detect | markdown_source | markdown_source.ext == html_file_name.ext slutregel ".html '=> detect_source do | r | sh "pandoc-s # r.source -o # r.name" slut
Om du är ny på lambda land eller inte har tänkt ut det helt ändå, här är en liten uppdatering. Procs är objekt du kan passera runt som kan utföras senare, så är lambdas. Båda är Proc-objekt, förresten. Skillnaden är subtil och kommer ner till de argument som överförs till dem. Lambdas kontrollerar antalet argument och kan spränga med en Argument
därför bryter inte procs. Den andra skillnaden är vad gäller deras hantering av returer. Procs kommer ut ur det område där proc-objektet utfördes. Lambdas avslutar bara lambda omfattningen och fortsätter att utlösa nästa kod som är i linje, så att säga. Inte super viktigt här, men jag tänkte på nybörjare bland dig, det kan inte heller göra ont.
Detta är en kort lista över flaggor som du kan överföra för att rake uppgifter.
--regler
Visar dig hur Rake försöker tillämpa regler-ett spår för regler. Ovärderlig om du hanterar ett par regler och löper in i fel.
$ rake quartermaster_gadgets.html --rules Att försöka regel quartermaster_gadgets.html => quartermaster_gadgets.md (quartermaster_gadgets.html => quartermaster_gadgets.md ... EXIST) pandoc -s quartermaster_gadgets.md -on quartermaster_gadgets.html
-t
Kom ihåg det solve_bonnie_situation
uppgift från artikel ett? Låt oss lägga till denna flagga till denna Rake-uppgift och aktivera spårning. Vi får också en backtrace om vi stöter på fel. Det här är verkligen användbart för debugging.
$ rake solve_bonnie_situation -t ** Invoke solve_bonnie_situation (first_time) ** Invoke get_mr_wolf (first_time) ** Utför get_mr_wolf Du har inte något problem Jules, jag är på den! Gå in där och slappna av dem och vänta på vargen som borde komma direkt! ** Invoke calm_down_jimmy (first_time) ** Utför calm_down_jimmy Jimmy, gör mig en tjänst, vill du? Jag luktade lite kaffe tillbaka där. Skulle du göra mig en kopp? ** Invoke figure_out_bonnie_situation (first_time) ** Execute figure_out_bonnie_situation Om jag blev informerad korrekt tickar klockan. Är det rätt Jimmy? ** Invoke get_vince_vega_in_line (first_time) ** Utför get_vince_vega_in_line Kom igen? Få det rakt buster. Jag är inte här för att säga tack! Jag är här för att berätta vad du ska göra! ** Invoke clean_car (first_time) ** Utför clean_car Jag behöver dig två fällor för att ta dessa rengöringsmedel och rengöra insidan av bilen. Jag pratar snabbt, snabbt, snabbt! ** Invoke clean_crew (first_time) ** Utför clean_crew Jim, tvålen! OK. herrar, ni båda varit länet innan jag är säker. Här kommer det! ** Invoke get_rid_of_evidence_at_monster_joes (first_time) ** Utför get_rid_of_evidence_at_monster_joes Så vad är det med outfitsna? Går ni till ett volleybollspel eller något? ** Inaktivera drive_into_the_sunrise (first_time) ** Utför drive_into_the_sunrise Ring mig Winston! ** Utför solve_bonnie_situation Du vet att jag skulle gå till frukost. Känner som att ha frukost med mig?
Miljö Rake.application.options.trace_rules = true
i en Rakefile säger sig själv att Rake visar oss spåra information om regler när vi kör en uppgift. Det här är coolt, för när vi kör ett spår via rake -t
, med en enda flagg får vi all information om felsökning som vi behöver. Vi får inte bara en lista över uppdragsinfordringar utan kan också se vilka regler som tillämpas eller försökte.
-P
Visar en förteckning över förutsättningar för alla uppgifter. Här använder vi igen solve_bonnie_situation
uppgift. Om du lämnar utdata för andra uppgifter, skulle det här vara en utpekad produktion:
$ 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 ...
Om du är nyfiken, springa rake -P
. Ganska intressant utdata.
-m
Kör uppgifter som multitasks.
Låt mig presentera dig för multi aktivitet
metod. Detta kan hjälpa dig att snabba på sakerna lite, trots allt har vi flera kärnor på de flesta moderna datorer, så låt oss utnyttja dem. Självklart kan du alltid uppnå hastighetshöjningar genom att skriva fast kod som saknar fett, men det kan säkert ge dig något extra i parallell med att löpande uppgifter parallellt. Det finns dock fallgropar som vi också kommer att täcka.
De uppgifter vi utför hittills kör alla uppgifter i följd, en efter en. Det är en säker satsning om din kod är i ordning, men det är också långsammare. Om hastighet är viktig för en viss uppgift, kan vi hjälpa till lite genom multi-threading-uppgifter. Tänk på att sekventiell tillvägagångssätt är det bättre alternativet under vissa omständigheter.
Låt oss säga att vi har tre Rake-uppgifter som måste springa som en förutsättning för att kunna utföra en fjärde. Det här är fyra trådar, i grunden. I den större bilden av saker, när du kör flera applikationer - eller för att vara mer specifika, är processer på en gång samma idé på jobbet.
multitask: shoot_bond_movie => [: shoot_car_chase,: shoot_love_scene,: shoot_final_confrontation] lägger "Huvudfotografering är klar och vi kan börja redigera." slutet
Använder sig av multi aktivitet
, Beroendet i vår förutsättningsserie är nu inte utförd i denna ordning längre. Istället sprider de sig och går parallellt - men före shoot_bond_movie
uppgift, förstås. En Ruby-tråd för varje uppgift kommer att köras samtidigt. När de är färdiga, shoot_bond_movie
kommer att göra sin verksamhet. Hur uppgifterna fungerar här liknar randomisering, men i själva verket utförs de bara samtidigt.
Den knepiga delen är bara för att säkerställa att vissa beroenden behandlas i en ordning som passar dina behov. Därför måste vi ta hand om tävlingsförhållandena. Detta innebär i grunden att en viss uppgift löper in i problem, eftersom orderna för utförandet hade oavsiktliga biverkningar. Det är en bugg.
Om vi kan undvika det uppnår vi trådsäkerhet. När det gäller gemensamma förutsättningar, intressant, kommer dessa förutsättningar att köras enbart eftersom multitaskförutsättningarna väntar på att de är färdiga först.
uppgift: shoot_love_scene do ... slutuppgift: prepare_italy_set gör ... slutuppgift: shoot_car_chase => [: prepare_italy_set] gör ... slutuppgift: shoot_final_confrontation => [: prepare_italy_set] gör ... slut multitask: shoot_bond_movie => [: shoot_car_chase,: shoot_love_scene,: shoot_final_confrontation ] sätter "Principal fotografering är klar och vi kan börja redigera." slutet
Både shoot_car_chase
och shoot_final_confrontation
uppgifter beror på prepare_italy_set
att avsluta först - som bara körs en gång, förresten. Vi kan använda den mekanismen för att förutse ordern när vi kör uppgifter parallellt. Lita inte bara på exekveringsordningen om det på något sätt är viktigt för din uppgift.
Nåväl, du antar att du nu är fullt utrustad för att skriva några seriösa Rake-affärer. Att korrekt använda detta verktyg kommer förhoppningsvis att göra ditt liv som en Ruby-utvecklare ännu mer glädjande. I denna andra artikel hoppas jag att jag skulle kunna förmedla vad ett enkelt men underbart verktyg Rake verkligen är. Det skapades av en äkta mästare av hans hantverk.
Vi är alla skyldiga Jim Weirich enorm respekt för att komma med detta eleganta byggverktyg. Ruby-samhället är verkligen inte riktigt detsamma sedan han gick bort. Jims arv är tydligen här för att stanna. En annan jätte vi är privilegierade att bygga på.