Hello and happy Memorial Day weekend!
Today I’ll be writing about my experience with exploring the Rails form helper
Working on a personal project, I’ve ran into a bit of a hurdle while refactoring the
resident.rb classes. My decision for implementing an Extract Class was based on my reading of Refactoring Ruby Edition, here’s what wikipedia has to say about when to consider using Extract Class:
In software engineering, the Extract Class refactoring is applied when a class becomes overweight with too many methods and its purpose becomes unclear. Extract Class refactoring involves creating a new class and moving methods and/or data to the new class.
Extract Class, the Setup
With the above in mind, I’ll show you a quick example of what my
resident.rb class looked like before I extracted some of the data (i.e. attributes) into their own respective classes.
This isn’t exactly everything I had originally in the
resident.rb class, and the attribute names may not match what’s current. The image above serves as an example to shed some light about my reasoning behind wanting to use Extract Class, and fix this nest of attributes. Can you spot some redundancies?
For me, I noticed multiple addresses and phone numbers. Also, with modularity in mind, what if I received requirements in the form of adding an additional doctor or emergency contact? Would I really want to add a list of
foot_care_doctor‘s attributes to my
resident.rb class? The answer for me is certainly not…
Thankfully, Sandi Metz’ book POODR has been an excellent start at purging my newbie thought process from this sort of thinking.
So I broke out the related attributes you see in the illustration above into their own respective classes. My results are as follows:
So this sounds, in theory, quite fine to me. I have extracted out
guardian.rb, among a few others for the purposes of refactoring
resident.rb to have one clearly (i.e. more clearly than before) defined responsibility.
Can You hear Me now
Communication between the classes! This is something that has been the biggest conceptual challenge for me since I’ve began learning to program. I am fairly certain that I can eagerly or lazily load objects in Ruby, but in Rails, I’m not so confident.
Starting with line: 8 in the example above, I’ve defined a hashed paramater of 0 or more attributes in
EmergencyContact‘s initializer. From what I understand, this provides a benefit of us not having to remember the argument order whenever we need to invoke a new
EmergencyContact object. On line: 18 there is something similar going inside of
Doctor. The biggest difference I notice between these two classes is that we are forcing parameter requirements inside of
Doctor‘s initializer. Not only their presence, but also their order and also giving each a default value.
The good news is that with these default values, comes an easing of argument order memorization. Every
Doctor object will come default with an
@address, and a set of phone/fax number instance variables. Our public interface, in doctor.rb, delegates the implementation of various functionality to each respective object, like lines:25, 29, 36 & 40 show, and all is well with the world.
DesignatedRepresentative class, I’m showing how I would attempt to lazily load an object from an external class. Keeping the retrieval objects private ensures me that I can’t use these methods for any other purpose than supporting the public interface above. The private methods each have conditional assignment operator’s inside of them.
These particular conditional assignment operators reads like so: If there is already an object assigned to this instance variable, then this
||= evaluates to
true, and the value inside the variable can be read or have its current value updated. And if there is no current
@primary_phone object, for example, then
||= will evaluate to
false and automatically assigns the code to the right of the conditional assignment operator. Which in this case, appears to be a invocation of some new objects. Then, as the private methods read, we assign these new objects to
So where does
accepts_nested_attributes_for fall into all of this? Well, I am going to have to continue that thought in greater detail in the next post, but lets get a quick primer since I have included the exploration of this form helper as my title and subject for this post, and as many future posts that will be needed to cover it.
According to the Rails API
accepts_nested_attributes_for defines an attribute(s) writer for specified association(s).
I have taken this to mean that we are given a shortcut method that allows us to simplify creating a writer method on an associated resource. I am not certain that my interpretation of the Rails API is correct, but lets make this assumption for the next few moments.
Would this mean
accepts_nested_attribtues_for is a replacement for the manual way of creating a writer method on the
Resident class, which delegates its implementation to one of the extracted class objects (i.e. PhoneNumber, Address, etc…)?
Here is an image of some console output to demonstrate that this works. It includes both native attributes as well as nested. Address is nested, and things like name, gender, and case number are native to Resident.
accepts_nested_attributes_for we can do the following inside our model to start things off:
Now we can make sure that we can tie our nested attributes to a certain form, in this case resident, we can head on over to our controller and do this:
And our form view uses the
fields_for :nested_attribute_names block to allow us to gain an ability to place attributes from another model (i.e. Address, Guardian, Doctor, etc…) on our Resident class. Here’s a quick sample from my project:
My current problem is that it isn’t apparent to me what the limitations of this form helper are. I have a Resident class which has defined decendant relationships with Doctor, EmergencyContact and Hospital. All of these classes utilize the polymorphic address relationship. I’ve left PhoneNumber as a direct association (instead of polymorphic) for the purpose of demonstrating that the challenge I’m having isn’t a result of the type of association I’ve defined.
I am still in the process of going over the documentation again, as well as exhausting my efforts of locating all related and relevant blog posts to determine why I can’t save or read a value for say,
:street_address for an emergency_contact, doctor, and a hospital.
I want to note that
accepts_nested_attributes_for responds as expected for Resident, which has led me to write this StackOverflow question late last week about what I’m not grasping properly.
One thing I found to be personally helpful while improving my understanding Rails, has been to find the manual way of doing something first, for a clear full picture of the problem area I’m working in. And then once that has been done to satisfaction, deciding whether or not utilizing the provided shortcut(s) are something that is palatable for me personally, or not.
I was reminded that I had learned this way of thinking while re-watching some of my pair programming sessions with Edward Anderson, specifically the videos where he instructs me on the usage of scopes and class methods.
As always I am open to any critiques or comments you may have, so feel free to stop by and say hello 🙂
And be on the look out for my next post about my exploration of Rails’
accepts_nested_attributes_for and my overall journey towards overcoming my challenge(s) of properly allowing my classes to communicate with each other while learning to follow OOP guidelines.