2010-05-23 3 views
31

Esiste un modo per sovrascrivere uno dei metodi forniti da un'associazione ActiveRecord?Rails: sovrascrivere il metodo di associazione ActiveRecord

diciamo per esempio ho il seguente has_many tipico polimorfa: attraverso l'associazione:

class Story < ActiveRecord::Base 
    has_many :taggings, :as => :taggable 
    has_many :tags, :through => :taggings, :order => :name 
end 


class Tag < ActiveRecord::Base 
    has_many :taggings, :dependent => :destroy 
    has_many :stories, :through => :taggings, :source => :taggable, :source_type => "Story" 
end 

Come probabilmente sapete questo aggiunge tutta una serie di metodi associati al modello di storia come i tag, i tag < <, tag = , tags.empty ?, ecc.

Come ignorare uno di questi metodi? In particolare i tag < metodo <. È abbastanza semplice sovrascrivere un normale metodo di classe, ma non riesco a trovare alcuna informazione su come sovrascrivere i metodi di associazione. Fare qualcosa come

def tags<< *new_tags 
    #do stuff 
end 

produce un errore di sintassi quando viene chiamato così ovviamente non è così semplice.

+2

A cosa stai cercando di fare questo? Questo potrebbe finire per rompere le altre funzionalità di ActiveRecord e probabilmente c'è un modo migliore per fare ciò che stai cercando. – Gareth

risposta

53

È possibile utilizzare blocco con has_many per estendere la vostra associazione con metodi. Vedere il commento "Utilizzare un blocco per estendere le associazioni" here.
Anche l'override dei metodi esistenti funziona, non so se sia comunque una buona idea.

has_many :tags, :through => :taggings, :order => :name do 
    def << (value) 
     "overriden" #your code here 
     super value 
    end  
    end 
+0

Ovviamente! Dimenticato di quello Questo è probabilmente il modo migliore per fare ciò che stai cercando. –

+1

E come chiameresti il ​​metodo originale? (Voglio sovrascrivere il metodo di compilazione, aggiungere alcune impostazioni predefinite e quindi chiamare l'originale) –

+1

@EranKampf non 'super' fare il trucco? – lulalala

0

Penso che volessi def tags.<<(*new_tags) per la firma, che dovrebbe funzionare, o il seguente che è equivalente e un po 'più pulito se hai bisogno di sovrascrivere più metodi.

class << tags 
    def <<(*new_tags) 
    # rawr! 
    end 
end 
+0

Non penso che funzioneranno entrambi. Sembra che tu stia suggerendo di estendere la Eigenclass del valore restituito dal metodo 'tags'. –

+0

Sta definendo un metodo all'interno dell'eigenclass di ciò che viene restituito da 'tags', che è probabilmente un array. Questo ha l'effetto di aggiungere un nuovo metodo di istanza all'array, che è quello che ho capito della domanda originale da porre. 'extend' ha un significato specifico in ruby ​​e non è quello che sta succedendo qui. – x1a4

+0

Hai ragione, questo è esattamente quello che sta facendo. Immagino di non aver capito dove stavi suggerendo di inserire quel codice. Ad ogni modo, credo di aver risposto più o meno la stessa cosa, solo con un po 'più di contesto. –

0

Si dovrà definire il metodo di tag per restituire un oggetto che ha un metodo <<.

Si potrebbe fare così, ma io davvero non lo consiglierei. Faresti molto meglio semplicemente aggiungendo un metodo al tuo modello che fa ciò che vuoi piuttosto che provare a sostituire qualcosa che usa ActiveRecord.

Questo esegue essenzialmente il metodo tags predefinito aggiunge un metodo < all'oggetto risultante e restituisce tale oggetto. Questo può essere una risorsa po intensiva perché crea un nuovo metodo ogni volta che si esegue

def tags_with_append 
    collection = tags_without_append 
    def collection.<< (*arguments) 
    ... 
    end 
    collection 
end 
# defines the method 'tags' by aliasing 'tags_with_append' 
alias_method_chain :tags, :append 
0

Il metodo che uso è di estendere l'associazione. Potete vedere il mio modo di gestire 'quantita' attributi qui: https://gist.github.com/1399762

Esso permette in sostanza di fare proprio

has_many : tags, :through => : taggings, extend => QuantityAssociation 

senza sapere esattamente che cosa il vostro sperando di raggiungere ridefinendo i metodi la sua difficile sapere se si potrebbe fare lo stesso

0

Questo potrebbe non essere utile nel tuo caso, ma potrebbe essere utile per gli altri che lo esaminano.

associazione richiamate: http://api.rubyonrails.org/classes/ActiveRecord/Associations/ClassMethods.html

esempio dalla documentazione:

class Project 
    has_and_belongs_to_many :developers, :after_add => :evaluate_velocity 

    def evaluate_velocity(developer) 
    ... 
    end 
end 

vedere anche Associazione estensioni:

class Account < ActiveRecord::Base 
    has_many :people do 
    def find_or_create_by_name(name) 
     first_name, last_name = name.split(" ", 2) 
     find_or_create_by_first_name_and_last_name(first_name, last_name) 
    end 
    end 
end 

person = Account.first.people.find_or_create_by_name("David Heinemeier Hansson") 
person.first_name # => "David" 
person.last_name # => "Heinemeier Hansson" 
17

Se si desidera accedere al modello stesso in Rails 3.2 si dovrebbe utilizzare proxy_association.owner

Esempio:

class Author < ActiveRecord::Base 
    has_many :books do 
    def << (book) 
     proxy_association.owner.add_book(book) 
    end 
    end 

    def add_book (book) 
    # do your thing here. 
    end 
end 

Vedi documentation

+0

questo è ancora il caso in Rails 5.1 – coconup

+0

@coconup questo non funziona per me in rotaie 5.1 - il metodo << sovrascrivente non viene chiamato affatto :-( – dowi

0

Rails guides documenti su sovrascrivendo direttamente i metodi aggiunti.

Il problema dell'OP con override << è probabilmente l'unica eccezione a questa, per cui segue the top answer. Ma non avrebbe funzionato con il metodo di assegnazione = o metodi getter.