Ruby ger en konstruktion som kallas en Module
som är ungefär som en tunn klass. Först kan Ruby devs titta på moduler som en” skräplåda ” med kod som bara kastas runt och används för att organisera filer, men det finns en metod för denna galenskap. Ruby-klassen Class
ärver från Module
och lägger till saker som instantiation, egenskaper, etc – allt du normalt skulle tro att en klass skulle ha. Eftersom Module
är bokstavligen en förfader till Class
, betyder det att moduler kan behandlas som klasser på vissa sätt.
viktigast att förstå är hur Ruby ancestor-kedjan fungerar. Liksom alla OOP-språk Stöder Ruby arv. När du ärver från en klass läggs superklassen till i din förfaderkedja. Du kan se förfäderna i vilken klass som helst genom att ringa ancestors
på den:
class Foo < BarendFoo.ancestors#=>
Som nämnts kan du hitta Module
i matrisen Class.ancestors
.
När du include
en modul i din klass läggs modulen till i klassens förfaderkedja – precis som en klass. Detta gör include
bara en form av arv, det finns inget speciellt med det.
module Barendclass Foo include BarendFoo.ancestors#=>
alla metoder i Bar
läggs till i Foo
som instansmetoder. Eftersom Bar finns i kedjan kan du till och med ringa super
från den för att ringa metoder ovanför den i kedjan, oavsett om de definieras på moduler eller klasser.
class Baz def hello p 'world' endendmodule Bar def hello p 'hello' super endendclass Foo < Baz include BarendFoo.new.hello#=> hello world
Även om Bar
är en modul, super
fortfarande ringer upp kedjan och så Baz#hello
kallas också. Det är värt att notera att Bar
läggs till förfaderkedjan framför Baz. När en modul ingår, läggs den alltid direkt ovanpå den inklusive klass. Detta kan bli förvirrande när du lägger till flera moduler, eftersom de ingår i” omvänd ” ordning:
class Foo include A include BendFoo.ancestors#=>
När A
ingår, infogas den direkt ovanför Foo
. Men när B
ingår, sätts den också in direkt ovanför Foo
, så det slutar landa innan A
.
include vs extend
include
är lätt att förstå, det lägger modulens metoder som instans metoder för att det är inklusive klass. Du kan tänka dig att extend
gör samma sak, men istället lägger till metoderna som klassmetoder.
module Bar def hello p 'hello' endendclass Foo extend BarendFoo.hello # no .new!# => 'hello'
prepend
förutom att inkludera/förlänga, Ruby 2.0+ lägger till prepend
metod. prepend
liknar include
, men lägger istället in modulen före inkluderingsklassen i arvskedjan.
class Foo include Bar prepend BazendFoo.ancestors#=>
i Ruby kan du öppna någon klass igen och omdefiniera metoderna på den-det verkar inte vara användbart att åsidosätta metoder på en klass med en prepended modul i motsats till att omdefiniera dem. Där prepend
kommer till nytta är när du fortfarande behöver den ursprungliga implementeringen av metoden, eftersom prepended-moduler kan åsidosätta metoder men ändå använda super
för att ringa den ursprungliga implementeringen.