make_cvar_alias($caller_package, %params)      if $params{cvars_alias};
   make_cvar_by_configs($caller_package, %params);
   make_cvar_by_name($caller_package, %params);
+  make_cvar_as_hashref($caller_package, %params);
+  make_cvar_value_parser($caller_package, %params);
 }
 
 sub save_meta_info {
 sub make_cvar_accessor {
   my ($caller_package, %params) = @_;
 
-  my @module_filter = $params{module} ?
-    ("config_id" => [ \"(SELECT custom_variable_configs.id FROM custom_variable_configs WHERE custom_variable_configs.module = '$params{module}')" ]) :
+  my $modules = ('ARRAY' eq ref $params{module}) ?
+      join ',', @{ $params{module} } :
+      $params{module};
+  my @module_filter = $modules ?
+    ("config_id" => [ \"(SELECT custom_variable_configs.id FROM custom_variable_configs WHERE custom_variable_configs.module IN ( '$modules' ))" ]) : # " make emacs happy
     ();
 
   $caller_package->meta->add_relationships(
     my $cvars       = $self->custom_variables;
     my %cvars_by_config = map { $_->config_id => $_ } @$cvars;
 
-    my @return  = map { $cvars_by_config{$_->id} || _new_cvar($self, %params, config => $_) } @$configs;
+    my @return = map(
+      {
+        if ( $cvars_by_config{$_->id} ) {
+          $cvars_by_config{$_->id};
+        }
+        else {
+          my $cvar = _new_cvar($self, %params, config => $_);
+          $self->add_custom_variables($cvar);
+          $cvar;
+        }
+      }
+      @$configs
+    );
 
     return \@return;
   }
   }
 }
 
+sub make_cvar_as_hashref {
+  my ($caller_package, %params) = @_;
+
+  no strict 'refs';
+  *{ $caller_package . '::cvar_as_hashref' } = sub {
+    my ($self) = @_;
+    @_ > 1 and croak "not an accessor";
+
+    my $cvars_by_config = $self->cvars_by_config;
+
+    my %return = map {
+      $_->config->name => { value => $_->value_as_text, is_valid => $_->is_valid }
+    } @$cvars_by_config;
+
+    return \%return;
+  }
+}
+
+sub make_cvar_value_parser {
+  my ($caller_package) = @_;
+  no strict 'refs';
+  *{ $caller_package . '::parse_custom_variable_values' } =  sub {
+    my ($self) = @_;
+
+    $_->parse_value for @{ $self->custom_variables || [] };
+
+    return $self;
+  };
+
+  $caller_package->before_save('parse_custom_variable_values');
+}
+
 sub _all_configs {
   my (%params) = @_;
 
   require SL::DB::CustomVariableConfig;
 
-  $params{module}
-    ? SL::DB::Manager::CustomVariableConfig->get_all(query => [ module => $params{module} ])
-    : SL::DB::Manager::CustomVariableConfig->get_all;
+  SL::DB::Manager::CustomVariableConfig->get_all_sorted($params{module} ? (query => [ module => $params{module} ]) : ());
 }
 
 sub _overload_by_module {
   # value needs config
   $inherited_value
    ? $cvar->value($inherited_value)
-   : $cvar->value($params{config}->default_value);
+   : $cvar->value($params{config}->type_dependent_default_value);
   return $cvar;
 }
 
 This is a Rose::DB::Object::Relationship accessor, generated for cvars. Use it
 like any other OneToMany relationship.
 
+Note that unlike L</cvars_by_config> this accessor only returns
+variables that have already been created for this object. No variables
+will be autovivified for configs for which no variable has been
+created yet.
+
 =item C<cvars [ CUSTOM_VARIABLES ]>
 
 Alias to C<custom_variables>. Will only be installed if C<cvars_alias> was
 Useful for print templates. If the requested cvar is not present, it will be
 vivified with the same rules as in C<cvars_by_config>.
 
+=item C<parse_custom_variable_values>
+
+When you want to edit custom variables in a form then you have
+unparsed values from the user. These should be written to the
+variable's C<unparsed_value> field.
+
+This function then processes all variables and parses their
+C<unparsed_value> field into the proper field. It returns C<$self> for
+easy chaining.
+
+This is automatically called in a C<before_save> hook so you don't
+have to do it manually if you save directly after assigning the
+values.
+
+In an HTML form you could e.g. use something like the following:
+
+  [%- FOREACH var = SELF.project.cvars_by_config.as_list %]
+    [% HTML.escape(var.config.description) %]:
+    [% L.hidden_tag('project.custom_variables[+].config_id', var.config.id) %]
+    [% PROCESS 'common/render_cvar_input.html' var_name='project.custom_variables[].unparsed_value' %]
+  [%- END %]
+
+Later in the controller when you want to save this project you don't
+have to do anything special:
+
+  my $project = SL::DB::Project->new;
+  my $params  = $::form->{project} || {};
+
+  $project->assign_attributes(%{ $params });
+
+  $project->parse_custom_variable_values->save;
+
+However, if you need access to a variable's value before saving in
+some way then you have to call this function manually. For example:
+
+  my $project = SL::DB::Project->new;
+  my $params  = $::form->{project} || {};
+
+  $project->assign_attributes(%{ $params });
+
+  $project->parse_custom_variable_values;
+
+  print STDERR "CVar[0] value: " . $project->custom_variables->[0]->value . "\n";
+
 =back
 
 =head1 AUTHOR
 
-Sven Schöling E<lt>s.schoeling@linet-services.deE<gt>
+Sven Schöling E<lt>s.schoeling@linet-services.deE<gt>,
+Moritz Bunkus E<lt>m.bunkus@linet-services.deE<gt>
 
 =cut