vluchtige ActiveRecord objecten
Ik ben een simpele web applicatie met rails aan het bouwen. Niets bijzonders; wat berichtjes, een gastenboek en wat formulieren. Deze laatste formulieren hoeven niet opgeslagen te worden maar moeten verstuurd worden via e-mail. Dat versturen is geen probleem; recht toe recht aan ActiveMailer
. De uitdaging zit hem in het valideren van deze formulieren.
Uitdaging is misschien een beetje sterk uitgedrukt maar als je net het beheer van berichten met ActiveRecord
hebt gebouwd, is het een beetje jammer als je dan toch weer dingen gaat schrijven als:
if params[:email].nil? || !EMAIL_PATTERN.match(params[:email]) @errors[:email] = 'E-mail adres moet correct ingevuld worden.' end
Neeh, daar pas ik voor! Ik wil gewoon form.valid?
kunnen vragen zoals bij ActiveRecord
objecten. Maar hoe doe je dat?
ActiveRecord
classes vragen aan de database welke kolommen er in een record zitten. Omdat het type object waar ik mee aan de slag wil helemaal geen records opslaat wil ik dus ook geen tabel aanmaken. Op de Rails Weenie site vond ik een hint voor m’n oplossing. Door alleen self.columns
te overschrijven kan je het standaard gedrag van AR overschrijven. Onze eigen implementatie moet dan alleen zelf zorgen dat self.columns
een lijst column definities terug geeft.
class MyForm < ActiveRecord::Base def self.columns [ ActiveRecord::ConnectionAdapters::Column.new('email', nil), ActiveRecord::ConnectionAdapters::Column.new('message', nil) ] end validates_presence_of :email, :message end
Dit werkt! Zolang we natuurlijk geen form.save
proberen natuurlijk. Maar de aanpak in de eerder genoemde hint is een stuk eleganter. Het kan natuurlijk nog beter:
class ActiveForm < ActiveRecord::Base def self.columns; @columns ||= []; end def self.column(name, sql_type = nil, default = nil, null = true) columns << ActiveRecord::ConnectionAdapters::Column.new( name.to_s, default, sql_type.to_s, null) end end
Hiermee kunnen we MyForm
als volgt schrijven:
class MyForm < ActiveForm %w{email message}.each { |c| column c } validates_presence_of :email, :message end
en m’n controller actie:
def feedback @form = MyForm.new(params[:form]) if request.post? && @form.valid? MyMailer.deliver_feedback(@form) redirect_to :action => 'thanks' end end
Auw, ik trek krom van geluk!
hmmm ik tel drie regels in je aller eerste voorbeeld en een heleboel ingewikkelde regels in je uiteindelijke oplossing. Oke DRY maar aangezien je verder geen Repeat hebt volgens mij is de eerste oplossing misschien wel zo simpel.
In de User class die je bij acts_as_authenticated meekrijgt, wordt een extra column gewoon als attribute gedefinieerd:
waarna er ook validaties op uitgevoerd kunnen worden:
Is dat iets anders??
je kunt met attr_accessor ‘pseudo’/virtuele kolommen toevoegen aan je model
waarna je gewoon de normale validations kunt gebruiken. Zoiets:
Je kan
attr_accessor
gebruiken enself.columns
een lege lijst column terug laten geven:Validatie werkt maar je object gedraag zich niet helemaal als een AR object omdat je de
columns
lijst niet hebt. Deerror_messages_for
helper methode gebruikt bijvoorbeeld deze lijst om errors “op de juiste” volgorde te tonen. Deze helper toont voor de bovenstaande class nooit validatie fouten. Daarnaast verlies je de mogelijkheid omMyForm
automagisch op te bouwen in je view mbv de columns lijst.En de mailer in Rails heet ActionMailer ipv ActiveMailer ;)
Interessante post!
Ik ben op dit moment met hetzelfde bezig, en heb dit ook op zo’n beetje dezelfde manier geimplementeerd. Je gebruikt toch wel vaak een support / contact formulier in een applicatie.
Ik vraag me trouwens af of er geen nettere manier gebruikt kan worden. Het zou mogelijk zijn om een plugin te maken en dus gewoon daar van te erven.
Ik heb de
ActiveForm.rb
in delib
directory gezet en m’n form objecten zijn nu redelijk netjes. Zie m’n code snippet voor de gedocumenteerde code.Wat je beter kan doen is gewoon een in memory sqlite table te maken :
Cool! Wel jammer dat SQLite dan ook geinstalleerd moet worden. Daar wordt m’n klant niet echt gelukkig van.
Over je hosting weet ik niks, maar als je op linux servers, of freebsd zit.
Is sqlite geen enkel probleem. En de installatie stelt helemaal niks voor.
Die is voor de meeste hostingproviders een stuk minder erg als rails :)