Rails Gear Pool Project

Jackson Reynolds
4 min readOct 4, 2020

I just finished my Rails portfolio project. I say that because I thought I was done a few days ago, but somehow things kept cropping up as I went down the list of specs we got from Flatiron School for this project. Little changes to one file would have rippling repercussions for routes and helpers methods across the app, and I’d be chasing bugs for a couple hours before everything became cohesive again. I even started recording my app walkthrough, but had to stop twice because I found a bug that I hadn’t anticipated.

Things seem to be working smoothly now, and I seem to have all the specifications included that are required. Still, this brings to mind something a previous employer mentioned when talking about installing a new door on his house. When he got to his choice of locks, he said, “They only keep out the honest.” With that in mind I’m sure my assessor will find ways to play with the code and expose weaknesses.

Onto the actual project… In our online cohort our instructor was walking us through building a rails app that modeled an online pantry, were the user could add items and measurements of items. I decided to take a similar concept, a gear closet, and apply it to a rental application. In Gear Pool, users can rent gear from a communal pool in bundles called gear lists. They can also donate items they own into the gear pool. Planning a Trip and donating an Item both rendered forms for the user to input information about each model.

In practice this meant defining the models as User, Trip, GearList and Item. Users and GearLists has many Trips, thus Trip served as my join table. Then, in order for a Trip to be associated with multiple items, GearList had many Items.

One challenge was the implementation of a form that could allow a user to create an instance of an Item as well as associate it with a GearList. Not only that, I wanted to allow the user to create a new GearList if the Item they were donating didn’t fit into any of the existing ones. I.e. if a user was donating their camp stove and there weren’t any cooking related GearLists yet.

Creating a form for the item was the place to start. After that, I ended up creating a select field that would take a collection of GearLists and allow the user to select which list (based on it’s name) that the item should belong to. Next came the input for a new GearLists name, should the user opt to create a new one. I used fields_for and an instance of a GearList that I built in the controller and passed into the view once rendered. The syntax looked something like this:

<%= form_for item do |f| %>
<%= f.label :name %>
<%= f.text_field :name %> <br>
<%= f.label :description %>
<%= f.text_area :description %> <br>
<%= f.label :condition %>
<%= f.text_field :condition %> <br>
<%= f.label “Select Gear List for item:” %>
<%= f.select :gear_list_id, @gear_lists.collect {|g| [g.name, g.id]}, {include_blank: true} %> <br>
<strong> — or — </strong> <br> <%= f.label “Create new Gear List” %> <br> <%= f.fields_for :gear_list do |g| %>
<%= g.label :name %>
<%= g.text_field :name %> <br>
<% end %>
<%= f.submit %>
<% end %>

Once I had this built and looking the way I needed it to, I knew that I would need a couple things in my model and controller to handle this behavior and not yield any unexpected results.

Firstly, I needed to define a method for Item that would take in the GearList properties and define a new instance that would reflect the input from the form and associate it with it’s Item in the same stroke. Rails includes a macro (implemented as accepts_nested_attributes_for :attr ) that creates this functionality quickly. This macro would define a method called gear_list_attributes= that would accomplish the above goal, as well as allow the fields_for method in the form to neatly pack the attributes for this new association into a nested hash called gear_list_attributes. This new method would then use the input form the hash to define the new GearList. However, I knew that in the case of a user picking from the list of existing lists, the gear_list_attributes[name] would still exist and be passed into params for the controller with a value of an empty string.

The problem being, the macro above doesn’t differentiate between blank fields or otherwise. If I let this be the case, the form would submit and everything would be peachy from the users point of view, until they arrived at the view page for the item and discovered that the GearList they picked ended up being ignored and a new GearList called.. nothing.. took it’s place. So the solution was to define a custom attribute writer that would check that a name was present, then associate a new GearList. This took the form of the method below:

def gear_list_attributes=(gear_list_attributes)
if gear_list_attributes[:name].present? && gear_list_id.blank?
self.build_gear_list(name: gear_list_attributes[:name])
end
end

The if condition above uses the present? String class method available in Rails to check if anything was put into the Gear List Name input field. However, I also needed to make sure that a GearList hadn’t been associated from the select tag included in the form. This is accomplished by calling the gear_list_id reader on an implicit self (being the Item instance) and making sure it was blank or nil.

I added an additional layer of protection in the controller by adding some logic to make sure the user didn’t try to select a GearList from the select tag and submit a name for a new one. I accomplished this by adding an if block that checked to see if there were inputs for gear_list_id and gear_list_attributes[name] present in the params. Code below:

if params[:item][:gear_list_id].present? && params[:item][:gear_list_attributes][:name].present?  @item.errors[:gear_list] << “Must EITHER create new gear list or choose an existing list”  @gear_lists = GearList.all
render ‘new’
end

--

--