> Developing DRY Forms

 > Projects

Home

Preface

This page discusses the motivations, development, and implementation of a new set of helper functions as a means to DRY out production of input forms. The task of DRYing components of a program should be intuitive for any developer. What sets this project apart is the usage of key features from the Ruby language to accomplish the task in an elegant manner.

The result of this project produces code that is easy for any developer to digest at a glance. These helper functions untangle both the logic required to persist input values between posts and the logic required to present error messages to a user. It essentially transforms code such as the following:


A Ruby on Rails view template built using HAML.

...into something easier to implement and more informative at a glance:


A Rails view template which produces the same markup as the prior figure.

This documentation describes the thought process of determining which pieces of the originating code can be abstracted into a function or set of functions. This begins with a brief discussion of the Rails framework and then transitions into the process of generalization. Using the generalized terms, the relevant methods are built and their components discussed.

Through this documentation, code blocks are given. Some code blocks have highlighted sections. These highlighted snippets emphasize the point being made within the textual content currently being discussed. This may be further emphasized within a code block's caption.

Care was taken to correctly use terms to a point of pedantry. Sometimes, this care was not enough to prevent potential confusion. For example, it may be hard to discern what is being conveyed in the statement, "The correct value is assigned to the value attribute." To help make this digestible, any term that refers to some literal code string will be marked up using a set of code-blocks. These terms will appear to the reader in a font type that looks mechanical.

The correct value is assigned to the value attribute.

Some of the code strings that are embedded within textual content are quite long. Because of this, a word wrap needs to occur within these strings to ensure that this page can be viewed on a wide range of device sizes. This will introduce a hyphen within these strings. Be wary of this; any code reference within a paragraph of textual content does not actually include the hyphen character.

Expressions given in code-blocks proper will not have any sort of word break. Instead, these groupings of code are presented in an element which can be scrolled horizontally and vertically. The size of the code contained within may be resized using the Code Size button in the top-right of the element. These elements may also be expanded and contracted by dragging the bottom right corner of the element.

tldr; The structure of this page:

The Framework Context section is useful for those who are not completely familiar with the Ruby on Rails MVC framework. This section loosely describes an example of a model, its controller, and a relevant view. It also describes in-built helper methods which many Ruby developers use to produce HTML input forms.

The Extending Functionality section describes extra logic that is often required on top of using the in-built Rails helper methods. This logic accomplishes presenting error messages on a failed form submission. This logic also allows values to persist upon failed form submission.

The Developing New Form Helpers section is broken into two parts. The first subsection describes the process of generalizing the aforementioned logic into a new helper function. The second subsection explains the Ruby features used to make the implementation of this generalization elegant and succinct.

The sections that follow flesh out the implementation of the new helper functions. This eventually leads to the application - Using the new form helpers.

Finally, the concluding notes section discusses lack of address within this documentation. It also discusses a road map towards creating a RubyGem to allow a wider range of developers to easily make use of these helper methods.


Ruby on Rails - Developing DRY Forms

Framework Context

The Rails framework for Ruby acts as a layer of abstraction to help expedite the development process of building web applications which use the Model-View-Controller software pattern. It takes a programming-by-convention approach where the mere existence of a given object will infer the existence of some other object or subroutine without the developer's explicit definition. These implicit connections are often scaffolded by a generative commands within the framework where a developer can declare a model or controller name and the framework stubs the required files and routes for the framework to operate on.

Example of a scaffold command.

The usage of these commands, (and the subsequent stubbing of files), does not necessarily expose the implicit logic. A good example of this is in creating some model and a declaration of some route for said model will create the ability to use an implicit variable that acts as a URL path for some action to take. Generically, if some <model> exists, and a <route> for the model is declared in the routes configuration file, the framework allows a developer to leverage a name reference of <model>_<route>_path which which can be used elsewhere. An example is as follows:

Declaration of a route within config/routes.rb.

Usage of implicit users_new_path within a view.

This programming-by-convention approach requires a developer to be keen on the existence of these implications; It requires a developer to be keen on how these implications effect and scaffold features of the framework.

Deciding to generate a model where the developer gives a name that's plural is a case where convention can cause pain. Consider the prior usage of users_create_path. When the User model was generated, the singular form of the noun was given. From it, Rails used the plural form of the noun to build a controller and a view. What occurs when the plural of user is given during generation? While pondering this question, the reader is invited to look into the --force-plural flag for the rails generate command whilst also considering the Active­Support::Inf­lect­or::Inf­lect­ions class.

A team which is aware of these conventions can be very efficient. A team which is not can lead to a heap of problems. This necessitates clear communication and documentation. Sometimes, communication and documentation alone aren't sufficient. In these cases, another layer of abstraction can be introduced to make the development process even easier.

The "view" in Model-View-Controller is a means to untangle the output of front-end markup from controller logic. An alternative to leveraging a view template would be to intersperse output statements, (likely via the print call), within the controller itself. This likely cannot be done within the Rails framework as it would break the expected convention. Thus, this would be implemented within a ruby script that has some other access to some database component/abstraction. This software pattern is commonly referred to as Model-View-View-Model (MVVM).

When it comes to producing a templates, Rails opts to leverage a declared route and subsequent action to present a view whilst interacting with a model. The default template format is ERB (Embedded Ruby). An ERB file allows a developer to place normal HTML expressions. Any dynamic logic that needs to be generated from Ruby scripting is placed between a set of special tags:

Within an ERB template, Ruby code can be included using both <% %> and <%= %> tags. The <% %> tags are used to execute Ruby code that does not return anything, such as conditions, loops, or blocks, and the <%= %> tags are used when you want output. - Action View Overview - Rails Guides

Building a Template

Consider a registration form for a web app. The information submitted through this form is applied to a User model. This is established through the UsersController by taking the new action. The model, controller, and route are all declared as follows:

Full definition of a User model within app/models/user.rb.

Partial definition of the UsersController within app/­controllers/­users_­controller­.rb.

Introduction of users_create_path within config/routes.rb.

The controller action operates on a hash map arbitrarily called info. info is filled by a method called user_params. Here, the require and permit methods are chained to produce a one dimensional hash-map which contains a set of keys associated with the symbols provided in the permit method. Both the require and permit methods include validation features abstracted away from the developer of the view, in addition to the production of the hash-maps used within the action controller.

These chained methods assume the existence of a hash-map of a certain structure. This hash-map reflects what is posted to the controller action. Based on these method calls and the user model, it can be assumed that a user's email, password, a password confirmation, first name, last name, and phone number should be posted through an HTML form. The Rails framework expects the posted form data to be in a hash-map within params[:user]. params contains all post information, which can encapsulate information pertaining to different models. An input field which posts to <model>[<attribute>] will be placed into params[:<model>][:<attribute>]. Knowing this, an initial template can be built.

Initial ERB template contained within app/views/users/new.erb. This was built with respect to some of the model properties established in the User model.

The template above is the view which will be presented should the new controller action be invoked. Submission of this form will invoke the create controller action through an HTTP post. This template is close to minimum in terms of explicitly interacting with the Rails framework. These interactions are highlighted, and can also be noticed by the usage of the <% %> tags.

The decision to use a variable to represent the model name is a weak contingency for the case in which the model's name is changed, (via a migration, for example). This allows a maintainer of this view to quickly adapt to the change by having a single entry point of value designation. Using this category of value declaration is an early lesson to learn both in the study of Computer Science and coding which serves as a starting point to transition into more elaborate means of DRYing out this code.

Two other values are being output into this form via evaluation of some Ruby expression. The general concept of users_create_path has been discussed within the context section. The value of form_authenticity_token is assigned by the Rails framework and is required for validating the session state of a visitor to the website.

Introducing Rails Helpers

The natural evolution of this template will introduce the usage of built-in helpers such as those included in Action­View::Helpers. Specifically, the FormHelper and FormTagHelper namespaces. Specifically form_tag, label_tag, text_field, and password_field are used.

Prior view template which has had certain HTML elements replaced with helper methods that are defined within the Rails framework. These are highlighted in addition to a change in the datatype assigned to the model variable.

The form_tag helper puts into place the <form> opening and closing tags while also placing a couple of hidden input fields which take care of the session token authentication previously discussed. The label_tag helper creates a relevant set of <label> tags while the text_field and password_field helpers create a relevant set of input tags whose type attributes are set to text and password, respectively. It should be observed that the initial assignment of model has been turned into a symbol as a means to adhere to the conventions set place within the documentation of these methods. That is, text_field and password_field both expect a symbol to be supplied as an argument for their object and method parameters. This is contrary to the label_tag helper which expects a string.

The object parameter for these _field helpers refer to the object in which the view presumably exists. In this case, it's the User object. The method parameter refers to the attribute in which the input will be applied. The term method refers to the mutator/accessor method which will be invoked upon submission of the form. Note that the name attribute of the resultant HTML element will maintain the form of <model>[<attribute>]; the output of this updated template will be the same as that was output from the first naively built template!

What is the advantage of using these helpers? They begin to abstract away the syntax required of HTML. One needs not worry about closing open HTML tags for each call to these helpers. Possible mistakes of typing a tag's set of attributes and their values are mitigated; no longer does a developer need to type out class="...", placeholder="...", for="...", id="...", etc. The aspects of markup that are important, such as assigning a class to an element, are isolated for the developer as an argument to the relevant helper.

Introducing HAML

The above approach doesn't completely eliminate these syntactic aspects. The template includes a set of div and header tags, (i.e., h1 and h2), that are vulnerable to being opened but not closed. The helper methods themselves need to be enclosed in a set of tags which act as a mechanism for the ERB template to know that a Ruby expression should be evaluated. How can these HTML elements be abstracted away?

Luckily, such a technology already exists. HAML was created precisely for these reasons:

Both these code blocks produce the same content. The HAML block (second) is more concise than the ERB block (first).

HAML stands for HTML Abstraction Markup Language. Usage of HAML using Ruby on Rails requires its gem to be bundled with the project. Doing so will allow a controller to route to a view whose extension is .html.haml instead of .html.erb. Here, the markup abstractions can be leveraged to clean up a view's code and to prevent common mistakes that come from code reuse of literal strings. Converting the template to HAML will produce the following template:

A more concise HAML view template.

Extending Functionality

A key part of the model used in these examples is that some facet of the value placed in the HTML input needs to be true in order for a post to succeed. That is, server-side validation is in in place for certain attributes of the model.

The helper functions provided through Action­View::Helpers do not account for this. It is up to the developer to check for these cases where an invalid post is made. The developer also needs to decide if and how to provide the feedback that a breach of validation has occurred.

The required feedback manifests itself in two ways. The first is that an error message should be presented informing the user what went wrong. These messages are declared within the model's class. Using the controller, the message should be placed into the flash hash-map whenever a violation occurs. Which key each message is associated with is up to the developer and is defined within the relevant controller action. In the example of the create controller action, flash is occupied dependent on whether or not @user.valid? returns true. In the case where it returns false, @user.errors is placed into flash[:login]. The schema of @user.errors is {:<model attribute> => <error message>, ... }.

The other facet where feedback manifests itself is by repopulating input elements with the values the visitor had previously placed. For example, if a visitor who is registering tries to post a form where their password fields don't match, it would be annoying if they were presented with a blank form where they need to retype their email, first name, last name, and phone number.

A look at the create controller action informs us that this information is grabbed from a call to user_params. The resultant hash map is placed into flash[:info] and can be used to repopulate an input tag's value attribute.

In order to present these two tiers of feedback requires a set of conditionals to determine whether or not these values need to be placed. Consider the input associated with the email property of the User model:

Introduction of validation logic. The first group of highlighted lines ensures the value previously submitted is maintained within the form. The last group of highlighted lines is responsible for displaying an error message related to the failed validation.

The first three highlighted lines denote the logic of occupying an input element's value attribute. Observe the merge method in use within the text_field helper which merges the value hash-map with the hash-map that is initially being supplied as an argument to the helper's option parameter. Usage of a hash-map for value allows the case where an empty hash-map is evaluated by Rails which will be recognized as a nil and thus not set the HTML elements's value attribute. This is contrary to the potential behavior of supplying an empty string which may visually override the placeholder's value with said empty string.

The last two highlighted lines denote the logic of displaying an error message. This occurs inline as per the bootstrap classes being used whilst being placed in the parent div element. That is, the label HTML element produced from the initial label_tag helper, the input HTML element produced from the text_field helper, and the optional label HTML element produced from the second label_tag helper are placed in the same container horizontally. Both of these label tags point to the same input tag, allowing a user to select either to place the cursor into the relevant input form.

POST upon entering test#example.com:

Top image is the production of the two ActionView helpers: label_tag and text_field, in addition to the submit_tag and form_tag. The bottom image reflects the result of a form submission with invalid data being supplied. Take extra note that the invalid value of test#example.com is maintained after submission post.

The inclusion of this logic introduces 5 new lines of code for each input element required of the form. This can balloon in size for forms which require a larger quantity of input tags. For the template that has been pieced together on this page, this would require a total of 30 new lines of code. This presents an opportunity to DRY out this code by introducing a new set of helper methods.


Developing New Form Helpers

Generalizing calls to ActionView::Helpers

It's surprising the HTML pattern of bundling label and input elements isn't addressed within the Rails framework. A lot of effort is put in the framework to abstract away these type of details. The Action­View::Helpers namespace is good evidence of this.

Let's consider the case in which a developer wants to create a form input element and its associated label. Also consider the prior code-block example in which an input and its label is created for the email attribute of the User model. Generalizing this code will help expose patterns which will make it easy to DRY:

Beginning the process of abstracting common traits from the behaviors of the view logic.

The first surface-level observation of this generalized code is that a <Model Attribute> is a common element between the text_field helper and the label_tag helper. Peering past the surface, it is less obvious that the value assigned to model is another commonality between the two. This is less obvious simply because it's not highlighted in the code above.

Both of the ActionView helpers also contain a <Natural Statement of Attribute>. These represent some string which a visitor can read. These are not necessarily the same strings, thus should be considered distinct from each other.

Another piece of generalization involves the <Controller Action> access within the flash hash-map. A glance will lead a reader of this code to believe that the notion of a controller action is only associated with the case where an error message needs to be output. A look at the controller which fills the hash-map will disprove this claim. The controller action is filling two values within the hash-map. Both :login and :info.

Furthermore, the value property of the HTML input element could be manipulated by JavaScript if need be. The fact that value is handled similarly to a nullable object where it is merged to the option parameter of text_field allows this namespace, (within the flash hash-map), to technically be exclusive from any controller action. If this were a namespace which adheres to a more general convention leveraged by Rails, it would realistically be labeled something like :values instead of :info.

Continuing the process of abstraction.

One more abstraction can be made. Each call to a ActionView helper allows an argument of some set of options. These options are received as a hash-map where each key represents some property of the helper. These are typically HTML attributes, such as an element's style attribute. Both ActionView helpers in this context may require a different set of options which requires distinction.

To begin developing a new helper, the production of an HTML input element and its direct label element will be isolated from a potential error message being produced by an access to flash[:<Controller Action>]. This means that the first helper method will require a <Model>, <Model Attribute>, <Natural Statement for Attribute>, <Options for ActionView Helper>, <Options for ActionView Label>.

It should be observed that <Options for ActionView Helper> is more generic than <Options for ActionView Label>. We've derived the more generic <ActionView Helper> from a call to the text_field method. Using the example of the template built thus far, a call to the password_field method is also used. Indeed, a wider set of methods should be allowed. This implies one last abstraction:

The final significant piece of generalization. Observing that the helper method's name can be abstracted.

Extracting generalized values into implementation

Each <Helper Method> will ultimately be derived from Action­View::Hel­per­s::For­m­Hel­per. Specifically, those with the _field suffix. This will manifest itself in the namespace of Application­Helper::create_­form_­input_­field within app/­helpers/­application_­helper­.rb. Considering the generalization process in the previous section, the implementation of the new helper method is defined as such:

The almost complete create_­form_­input_­field helper method developed within app/­helpers/­application_helper­.rb. Missing logic is highlighted.

Initially, StandardError is extended to help inform a developer if an invalid symbol was provided as an argument. Whether or not this error is flagged hinges on the evaluation of the helper's first parameter - helper_sym. This evaluation occurs through a call to the suffix? method to determine whether the field suffix is being used, as noted prior.

The helper create_­form_­input_­field initially receives a symbol to represent the name of the ActionView helper that needs to be called. The next two parameters adhere to the parameter naming convention of the _field methods established within Action­View::Hel­p­ers::Form­Helper. The object parameter correlates to a view's model and the method parameter correlates to a model's attribute.

Near the end of the helper function, the label_tag helper is called to create the HTML string representative of the label element. The resultant string is assigned to a local variable. An inexperienced Ruby developer may struggle here in terms of knowing how to invoke the required helper represented by helper_sym. This is where the beauty of Ruby as a programming language comes into play.

A key to understanding how to solve this problem is that everything in Ruby is an object. Everything. Literals are objects. Class definitions are objects. More importantly, syntactic abstractions are object method calls. For example, using the assignment operator is transcribed to an invocation of that object's mutator method.

Invocation of an object's method is an abstraction on providing that object's send method the symbol of the method that needs to be called! This is a bit wordy, but is easy to understand through an example: The expression "Hello" + "World!" is equivalent to "Hello".­+("World!") which is also equivalent to "Hello".­send(:+, "World!"). This allows the implementation of a method which allows a caller to ask some object to invoke some unknown method. In the context of create_­form_­input_­field, this allows for the following expression:

Invocation of the send method on some unknown object.

For which object should this send method be invoked? Ruby on Rails documentation is not clear enough to personally find whether it addresses this question. This method is defined within a module which is invoked within a logical space that handles view rendering. Does this method definition exist outside this logical space? Personal intuition does not ignore the fact that some object exclusive from whatever is governing view rendering is responsible for governing the helpers. Does the object reference of that which manages ActionView need to be passed? What about the object reference of that which manages ApplicationHelper?

Documentation may lack, but Ruby's features can help in this regard. Recalling that everything is an object, every object inherits from some base class. This class allows for object introspection in which objects retain information about themselves. The first step into discovering which Rails object(s) govern these logical spaces is to simply ask each relevant scope what its class name is. That is, to execute a debugging print statement within the logic of both create_­form_­input_­field and the view template itself.

Within the view template, these statements were output to the page itself:

Simple/Lazy debugging statements within app/­views/­users/­new.­html.­haml.

Within the helper function, these statements were output to some text file:

Simple/Lazy debugging statements within app/­helpers/­application_­helper.rb's Application­Helper::create_­form_­input_­field.

Unfortunately, an empty string was produced for both calls to self.class.name. It seems the object(s) handling both these spaces was not given a class name. Fortunately, the preceding print statement asks to print the object for each space. These puts self expressions return the memory address of each object. Serendipitously, both spaces return the same memory address, meaning the same object handles both these logical spaces. This allows a reference to self to finish out the helper function:

The self object is the entity in which the parameterized method of helper_sym should be called.

Factoring more helpers from Action­View::Hel­p­ers

Valid and invalid symbols for create_­form_­input_­field

The above helper allows the creation of various HTML input elements. These elements are produced by calls to Action­View::Hel­p­ers::Form­Hel­per's methods which have the _field suffix. The means of validating the method symbol (:helper_sym) which create_­form_­input_­field receives is not robust. Indeed, if one sends a symbol with a suffix of _field which does not exist in Action­View::Hel­p­ers::Form­Hel­per which coincidentally also receives the same amount of arguments whose datatypes correlate to the order expected of the FormHelper methods then the method will be evaluated which may lead to unexpected behavior.

To account for this, Ruby object introspection can be leveraged. A call to Action­View::Hel­p­ers::Form­Hel­per's public_­instance_­methods method can be made where it can be determined if the symbol exists within this lot.

Looking at the return of public_­instance_­methods shows us a list of helper functions which correlates to the documentation for this module. Each entry with the _field suffix essentially behaves the same as far as the back-end is concerned. This was explored through the process of generalizing the usage of these helpers. There are a few helpers that exist in this namespace that do not adhere to this generalized behavior. form_for and convert_to_model are two such examples. These are intentionally caught by the validation in place in which the symbol is evaluated to contain the _field suffix.

There is a helper here that is caught by argument validation which behaves functionally the same as the others: text_area. This is caught on account of not having the _field suffix. Despite this, the set of parameters aligns with that of the other helper functions and the production of a call to text_area behaves the the same with respect to how it's processed on the back-end.

Realistically, text_area should be handled correctly with respect to create_­form_­input_­field's validation process by adapting the valid symbol check for :helper_sym. For the sake of the project which spurred the development of these helper methods, an alternative approach was taken.

Action­View::Hel­p­ers::Form­Tag­Hel­per

An astute developer will notice the use of label_tag within create_­form_­input_­field. They will have been correlating what's been said on this page with the documentation for Action­View::Hel­p­ers::Form­Hel­per and observe that label_tag does not exist within this module. In its place is label, which would not pass the valid symbol check discussed for create_form_input_field. What's the difference between these two modules? What's the difference between label and label_tag?

Provides a number of methods for creating form tags that don’t rely on an Active Record object assigned to the template like FormHelper does. Instead, you provide the names and values manually. - Action View Form Tag Helpers - Rails Docs

The decision to leverage label_tag whilst building the template and the resultant helper function was to make the above equivalence more intuitive for the reader of this article. A reader of the documentation for FormTagHelper will take note that all the instance methods have the _tag suffix. This includes a text_area_tag.

The project which spurred the development of these new helper functions was influenced by the convenience of abstracting away markup from back-end development. This as in an effort to minimize differences in front-end developmental styles and approaches. Minimization that occurs by development and implementation of these new helper functions. A decision was ultimately made to prioritize usage of ActionView helper functions with the _tag suffix instead of the _field suffix. The primary motivator for this was the fact that all the instance methods for Action­View::Hel­p­ers::Form­Tag­Hel­per have the _tag suffix, making argument validation an easier task. This leads to the next helper function:

New helper method create_­form_­input_­tag which operates with the context of the instance methods contained in Action­View::Hel­p­ers::Form­Tag­Hel­per.

Like create_­form_­input_­field being derived from FormHelper, create_­form_­input_­tag adheres to the parameter naming convention of the instance methods within FormTagHelper. The above implementation applies more strict argument validation whilst attempting to guide a misuse by supplying the method an argument for helper_sym with the _field suffix.

It should be noted that all of the instance methods within FormTagHelper are functionally equivalent. Consider the entry for select_tag. This instance method has an important optional parameter for a collection of option_tags. Likewise, label_tag helper has an optional parameter which can be used to place a value which gets placed between the resultant HTML tags. The documentation labels the usage of this parameter as "content". Other instance methods, such as number_­field_­tag, will label this optional parameter as value.

A conventional conundrum occurs where the term value is used with these optional parameters. The value parameter is used to occupy the value attribute of the resultant HTML element. Here, the developer has a choice of supplying the value of this attribute using this helper argument or by using the options parameter as is done in the preceding conditional involving the flash hash-map. This value impasse is why the decision was made to nil this argument for the method call of helper_sym so that the intended behavior associated with the flash hash-map is not lost.

Deciding to provide a nil value to the call to helper_sym means that more argument validation needs to be put in place for create_­form_­input_­tag. This should catch the usage of the instance methods which should have a value provided as an argument to this parameter. A good example here is select_tag, which is composed of option tags provided as a hash-map to this argument.

The implementation of this validation is as follows:

New validation check to ensure proper values are being passed to helper_sym.

Helpers expecting a collection

The decision was made to continue using a helper function which supplies a nil argument to the third parameter to a call to an instance method within FormHelper or FormTagHelper. This results in needing to implement a set of conditionals that evaluate the value given for the helper_sym parameter which informs the user of the mistake and a potential path to take to fix said mistake. One of these messages will be returned upon a call where the value for helper_sym is :select_tag.

To address this, a helper method called create_­form_­input_­select was developed. This helper method reflects the same pattern that create_­form_­input_­field and create_­form_­input_­tag establishes, but expects an extra parameter that is a collection of attributes which can be used within a call to Action­View::Hel­p­ers::Form­Options­Hel­per's  options_­from_­collec­tion_­for_­select. It should be noted that create_­form_­input_­select no longer needs a helper_sym parameter on account of the fact that it is specialized to always produce a pair of select tags. That is, there is no need for a self.send as select_tag is used in its stead.

Helpers expecting error handling

Recall that with the latest iteration of the HAML template a set of conditionals are in place which check whether or not a post to the model succeeded. In the event that posting validation failed, a set of error messages were placed into the flash hash-map. A decision was made to implement this error functionality as a separate helper function. This decision was made to account for the fact that not every input element requires validation. An example of this to optionally require a phone number upon registration.

The resultant helper function has been labeled create_­form_­error. The internal logic is trivial as it simply checks the existence of a value representative of the validation's error message established in the relevant model. Recall that this message is placed in the flash hash-map within the relevant controller. In the example used thus far, error messages are placed into flash[:login]. If this helper method was inherit in the Rails framework, this realistically would be associated with a more generic key, such as :error instead of :login.

As is the case with create_­form_­input_­select, create_­form_­error does not require a helper_sym parameter. This method is only responsible for producing a label_tag should it need to be produced. The parameter for helper_sym is essentially replaced with a parameter to represent the key to the hash-map contained in flash. In this example, the key is :login.

A resultant call to create_­form_­error is as such:

A call to create_­form_­error within a HAML view template.

... where create_­form_­error is defined as follows:


Using the new form helpers

Reconsider the initial template pertaining to creating the first input element:

With the new set of helper methods in place, the code can be reduced to the following:

Consider the initial HAML template. Consider this template with all the conditionals required for a view to remember its prior value and the set of conditionals used to display a validation error message:

With the new form helpers in place, the above view can be reduced to the following:

This effectively DRYs out the code to a production that is easy for any developer to digest at a glance.


Concluding notes

It should be emphasized that the usage example above is not completely DRY. Indeed, a variable can be used to represent the various hash-maps being supplied as an argument to these helper functions. It is acknowledged that this would be the next course of action to take in terms of making general behavior easier to digest at a glance. For example, assigning {:class => "alert-danger input-group-text"} to a variable called error_styling would make it very easy for a back-end developer to plug data into a helper call. A front-end developer could assign these attributes to this variable at the top of the view or within some partial. This would further untangle responsibility and be generally more efficient.

Addressing this is trivial. What is important for this use-case is that the behavior is clearly communicated within the front matter of each statement through the method's name. create_­form_­input_­field and create_­form_­error does a good job indicating what each line is accomplishing.

Source Repository

The repository of the originating project is located here. It'll be easy to observe that the work done here was published at an earlier time. Indeed, some of the implementation differs from what's offered within the documentation provided here. This is due to the fact that, as this page was written out, some new ideas came to mind to improve the overall implementation.

Future Development

Facets of non-trivial improvement stem from how to make this set of helper functions more modular. Addressing these routes of improvement will lead to the development and packaging of a gem such that other developers can easily use these features. The road-map for this development is as follows:

  • Allow for the acceptance of an optional parameter to accept collections and/or extraneous values within create_­form_­input_­field and create_­form_­input_­tag. For example, allow :select_tag to be a valid argument for create_­form_­input_­tag. This will require investigation in to how the existing helper functions within Action­View handle their optional parameters.
  • Create helper methods to act as wrappers for the symbols that are filtered within the incompatible? sub-method.
  • Provide a means for create_­form_­error to be implicit within the helper methods developed; make it so that a call to create_­form_­error within a view is not necessary as a means to produce an error.
    • Intuition which leans into an easy solution for this problem is to have a simple boolean flag in place within create_­form_­input_­tag or create_­form_­input_­field which determines whether create_­form_­error should be called within. A more elegant solution would be to explore providing create_form_error as a callback to these helpers.

Action on this road-map will be presented once the gem is complete. Documentation pertaining to this gem will be provided in a new project page. This project page will link to this documentation and vice-versa.