Categories in a Rails App

Adding categories to an already existent Rails application

I'm implementing categories on my agenda for an agenda application I'm working on and a warning to rails purists I will use a scaffold for this project.

The Scaffold


You will hear that must rails purist are rabid against the scaffold; it is true that if you do not know what you are doing this technique will give you a ton of technical debt, a lot of useless files and can potentially break your application. You want to custom whatever the scaffold gives you! In this case, since I've created a bunch of applications entirely from scratch and even without the Rails Framework. I am confident of what I'm doing, and I also want to get this up and running fast so if it's a total fail I can delete the branch and approach my problem differently.

On the console:

$ rails g scaffold category name:string desc:text

Since I already have my meetings model, I will run this:

$ rails generate migration AddColumnsToMeetings category_id —force

Then:

$ rails generate migration AddColumnsToMeetings category_id —force

Another example:

$ rails generate migration AddColumnsToComments category_id —force

Inside the Models


When you think of active record, you always have to think of hierarchies, in this case, the categories are way up there in the food chain, like a regime that just appeared to control my Meeting model. So the foreign key is in charge of the Meeting model since it's a new key it had to be implemented by force. I have to define the relationship amongst the models:

#app/models/meeting.rb
belongs_to :category

And in the category model:

#app/models/category.rb
has_many :products

In the forms:


I need to modify the submission form because otherwise this work will have been for nothing and it will stay like must of my app in the back end. I want a drop down for the possible categories; I know the pains of not having this because I just tried for hours to implement a simple search form, and if your search bar has too many fields it's better just to create a search model. I know I will want a search model that will rely on categories and that I also want it to look a certain way I will use the select_tag field. This will give my users a drop down. The select_tag has needed an array as an argument, so I solve that using map

@categories = Category.all.map{|c| [ c.name, c.id ] }

Changes in the controller:

#app/controllers/meetings_controller.rb
def new 
    @meeting = Meeting.new 
    @categories = Category.all.map{|c| [ c.name, c.id ] }
end

Now to add the visible drop down this bit of code need to replace the text field Rails gives you by default:

app/views/meetings/_form.html.erb
<%= select_tag(:category_id, options_for_select(@categories), :prompt => “Select Category”) %>

Or this other bit of code

<%= f.collection_select :category_id, Category.all, :id, :name, prompt: true %>

Tweak The Controllers


My meetings controller now needs to be edited, there are two ways to go about this, a naïve implementation that would be changing the edit, update and create action, and that would look like this:

#app/controllers/meeting_controller.rb
@categories = Category.all.map{|c| [ c.name, c.id ] }
@meeting.category_id = params[:category_id]

The less naïve implementation is using strong params and before actions.

   def meeting_params
      params.require(:meeting).permit((...your previous permited params +...), :category_id)
    end

And the before action:

class MeetingsController < ApplicationController
  before_action :set_categories, only: [:show, :edit, :update, :destroy]

.
.
.
 private
    # Use callbacks to share common setup or constraints between actions.
def set_meeting
   @categories = Category.all.map{|c| [ c.name, c.id ] }
 end

The Views


To add a category column to the index view:

#app/views/products/index.html.erb
<td><%= link_to Category.find(meeting.category_id).name, category_path(meeting.category_id) %></td>

What I like about this method is that you avoid setting @category in the controllers, this separates concerns. Now to be able to link it to the categories in their in the show:

#app/views/categories/show.html.erb
<h3> Meetings under this category </h3>
<% Meeting.where(category_id: @category.id).each do |meeting| %>
<li> <%= meeting.name %> </li>
<% end %>