Project Ramon

A learning journey from a Ruby noob perspective

Exploring Rails: accepts_nested_attributes_for – pt[0]. Setting up the scene

nested_attributes_header_img0

Hello and happy Memorial Day weekend!

Today I’ll be writing about my experience with exploring the Rails form helper accepts_nested_attributes_for.

Working on a personal project, I’ve ran into a bit of a hurdle while refactoring the admin.rb and 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.

nested_attributes_for_1_img1

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 em_contact3‘s or 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 phone_number.rb, address.rb, doctor.rb, emergency_contact.rb and 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.

In the 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 @primary_phone.

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_for1_img2

Now using 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:

nested_attributes1_img3

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.

In Conclusion

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.

Stay tuned…

Advertisements

Categories: Newbie, Ruby on rails, Uncategorized

1 reply

Trackbacks

  1. Exploring Rails: accepts_nested_attributes_for pt[1] | Project Ramon

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out / Change )

Twitter picture

You are commenting using your Twitter account. Log Out / Change )

Facebook photo

You are commenting using your Facebook account. Log Out / Change )

Google+ photo

You are commenting using your Google+ account. Log Out / Change )

Connecting to %s