La mia domanda riguarda i metodi del controller (possibilmente inclusi da una classe esterna) che funzionano con le variabili di istanza. Uso spesso un before_filter nei controller per impostare determinate variabili, ad esempio:
class DocumentController < ApplicationController
before_filter :fetch_document
def action
@document.do_something
end
private
def fetch_document
@document = Document.find(params[:id])
end
end
Ho lavorato a un progetto in cui alcunii controller condivideranno alcune funzionalità, ad esempio la modifica dei documenti. Il mio primo pensiero è stato quello di estrarre i metodi pertinenti e ottenerli da application_controller.rb o da un modulo separato. Ma poi ho notato che stavo scrivendo un codice simile a questo:
def fetch_document
@document = Document.find(params[:id])
end
def do_something_to_document
@document.do_something
end
Questo fa scattare campanelli d'allarme:do_something_to_document essenzialmente presuppone l'esistenza di @document, piuttosto che prenderlo come argomento. Secondo le tue saggi opinioni, questa è una cattiva pratica di programmazione? O sono paranoico?
Supponendo che sia un problema, vedo due approcci generali per affrontarlo:
Controlla l'istanza var e bail a meno che non sia impostata:
def do_something_to_document raise "no doc!" unless @document [...] end
Chiama l'azione con l'istanza var come argomento:
def do_something_to_document(document) [...] end
2 ha un aspetto migliore, perché nasconde il contesto dil'oggetto chiamante. Ma do_something_to_doc verrà chiamato solo dai controller che hanno già impostato @document, e prendere @document come argomento del metodo comporta l'overhead della creazione dell'oggetto. (Giusto?) 1 sembra un hacker, ma dovrebbe coprire tutti i casi.
Sono propenso ad andare con 1 (ammesso che abbia ragionesul problema delle prestazioni), anche se vedere un elenco di metodi che fanno riferimento a variabili di istanze misteriose mi dà degli alveari. Pensieri? Fammi sapere se posso essere più chiaro. (E ovviamente, se si risponde a questa domanda da qualche parte non l'ho visto, indicami la giusta direzione ...)
Grazie, -Erik
risposte:
0 per risposta № 1Se hai davvero bisogno di documenti in controller diversi, farei qualcosa del genere:
class ApplicationController < ActionController::Base
private
def document
@document ||= Document.find(params[:document_id])
end
end
class FooController < ApplicationController
before_filter :ensure_document, :only => [:foo]
def foo
document.do_something
end
private
# TODO: not sure if controller_name/action_name still exists
def ensure_document
raise "#{controller_name}##{action_name} needs a document" unless document
end
end
0 per risposta № 2
Poiché @variable è una variabile di sessione / istanza, otterrai un'eccezione nulla in do_something_to_document metodo.
Il primo codice va bene, perché before_filter caricherà sempre il tuo @document.
Ti suggerisco di scrivere qualcosa del genere
def fetch_document(doc_id)
@document ||= Document.find(doc_id)
end
def do_something_to_document
my_doc = fetch_document(params[:id])
end
dove do_something_to_document è nel controller (in caso contrario, non utilizzare params [:id], anche se sai di poter accedere a questo globale, usa un altro parametro esplicito). La cosa || = assicurerà che chiamerai la base solo una volta su richiesta.