protected attributes en de form methode
Door m’n werk aan de dutchify plugin en met name de dynamische scaffold, heb de form
methode van ActiveRecordHelper ontdekt. Deze functie bakt voor een ActiveRecord instance een HTML formulier en haalt daarmee veel onnodig werk uit handen.
Deze functie heeft echter een probleempje, het geleverde formulier bevat alle inhouds attributen. Dat is jammer als je allerlei programmatisch gevulde attributen hebt zoals created_at
en updated_at
. ActiveRecord geeft je de mogelijkheid om attributen te markeren als protected met de attr_protected
methode. Protected attributen worden niet in bulk assignments zoals new(attributes)
en attributes=(attributes)
meegenomen en omdat een typische CRUD controller deze methoden gebruikt, lijkt het logisch dat de form
methode deze weglaat of op z’n minst onschrijfbaar maakt. Jammer genoeg doet form
hier helemaal niets mee..
Enter :input_block
. Om attributen te renderen in een formulier kan je een blok code meegeven dmv van de :input_block
optie. Standaard wordt er een :input_block
gebruikt welke een <label>
en een <input>
, <textarea>
of <select>
renderd. Om protected attributen onschrijfbaar te maken gaan we een :input_block
bakken welke deze attributen anders behandelt en deze bijbehoren de <input>
etcetera tags disabled
.
Een :input_block
krijgt twee argumenten mee, de naam van de instance variable waarvoor het formulier opgebouwd wordt en het column object dat er mee geassocieerd wordt. Helaas weet een column object niet of deze protected is, dit kunnen we achterhalen dmv de class methode protected_attributes
van het record. Deze methode bevat een lijst symbols met de namen van alle protected attributes. De volgende methode bepaalt of, gegeven de naam van de instance variable en het column object, het attribute protected is:
def attr_protected?(record, column) o = instance_variable_get("@#{record}") o && o.class.protected_attributes && o.class.protected_attributes.include?(column.name.to_sym) end
Nu kunnen we onze eigen form
methode maken met een protected bewust :input_block
:
def my_form(record) form record, :input_block => Proc.new { |record, column| options = attr_protected?(record, column) ? { :disabled => true} : {} <<-"end_html" <p> <label for="#{record}_#{column.name}">#{column.human_name}</label> <br /> #{input(record, column.name, options)} </p> end_html } end
Plaats beiden methoden in een helper en met je kan met onze my_form
methode gemakkelijk formulieren maken waarin de protected attributen netje onschrijfbaar zijn gemaakt.
Maar we willen dit natuurlijk altijd voor formulieren die met form
worden gemaakt en dus dat dit ook voor dynamische scaffolds werkt. Een beetje graven in de ActiveRecordHelper code wijst uit dat de form
bij het missen van een :input_block
optie deze vraagt aan de default_input_block
methode. Als je deze methode zelf in je helper opneemt, gebruikt form
deze en wordt je eigen variant dus altijd gebruikt, ook door de dynamische scaffold!
Dus plaats het volgende in je application_helper.rb
en al je protected attributen zijn beschermd:
def attr_protected?(record, column) o = instance_variable_get("@#{record}") o && o.class.protected_attributes && o.class.protected_attributes.include?(column.name.to_sym) end def default_input_block Proc.new { |record, column| options = attr_protected?(record, column) ? {:disabled => true} : {} <<-"end_html" <p> <label for="#{record}_#{column.name}">#{column.human_name}</label> <br /> #{input(record, column.name, options)} </p> end_html } end
Prachtig! De attributen created_at
en consorten automagisch protected maken laten we open als huiswerk voor de lezer.