August 3, 2020
•Last updated November 5, 2023
Let's build for Ruby and Rails developers - Part 4
Creating the core data models
Welcome to part 4 of my Let's build for Ruby and Rails developers series. This part will focus on generating our core model layer inside the Ruby on Rails application.
Find the code for this session here or by clicking the button in the sidebar to the right.
Data modeling
The core models we'll leverage in this app, to begin with, will be the Job
and User
model. In a previous part, I mentioned taking a simpler route with the User
model that prefers boolean
attributes as to a fully separated persona of user models.
Our User
model ends up with a few sets of boolean columns including:
developer
admin
employer
We got there by generating a new migration:
rails g migration add_personas_to_users developer:boolean employer:boolean
Inside the migration file I added a default value to false
for each column:
# db/migrate/XXXXXXXXXXXXX_add_personas_to_users.rb
class AddPersonasToUsers < ActiveRecord::Migration[6.0]
def change
add_column :users, :developer, :boolean, default: false
add_column :users, :employer, :boolean, default: false
end
end
A given User
can be any combination of these. Coming up I'll create some helpful methods that make checking against these constraints easier to handle. We'll leverage these throughout the app.
Updating Devise
Because we are updating our User
model we need to permit additional parameters on via the Devise gem. This takes place in our application_controller.rb
file and looks like this:
# app/controllers/application_controller.rb
...
def configure_permitted_parameters
keys = [:name, :developer, :employer]
devise_parameter_sanitizer.permit(:sign_up, keys: keys)
devise_parameter_sanitizer.permit(:account_update, keys: keys)
end
With this intact, we can now pass these values from the front-end during user sign up and update actions.
Our views need some updates to include these new fields as well
<!-- app/views/devise/registration/edit.html.erb -->
<h3 class="mb-4 text-lg font-bold">Roles</h3>
<div class="input-group">
<%= f.check_box :developer %>
<%= f.label :developer, "Use railsdevs.com as a developer?" %>
</div>
<div class="input-group">
<%= f.check_box :employer %>
<%= f.label :employer, "Use railsdevs.com as an employer?" %>
</div>
<!-- app/views/devise/registration/new.html.erb -->
<h3 class="mb-4 text-lg font-bold">Roles</h3>
<div class="input-group">
<%= f.check_box :developer %>
<%= f.label :developer, "Use railsdevs.com as a developer?" %>
</div>
<div class="input-group">
<%= f.check_box :employer %>
<%= f.label :employer, "Use railsdevs.com as an employer?" %>
</div>
The Job
model
Our other core model is of course the Job
model. The main focus of this app is a job board for ruby and rails developers. We'll be paying the most attention to this model by design.
To save a lot of time I generated a scaffold for the Job resource.
Initially, we'll add some columns I know we need but later on, this might need to be tweaked. We can do that with additional migrations or go back in time using rails db:rollback
to tweak the initial migration. We'll cross that bridge once we get to it.
rails g scaffold Job company_name company_website compensation_range link_to_apply remote:boolean role_type title years_of_experience user:references
There's a lot going on in the code above but at its core, we'll be generating a full RESTful construct for the Job
model. This effectively creates a new database table called jobs
, and all the files relative to the MVC concept in a Ruby on Rails application. We'll get some extra files we don't need but that's okay for now. We can always do some housekeeping later.
Note the names I passed after Job
in the string above. Each of these will generate a new column on the jobs
database table. For each name, you can pass additional options prefaced by a :
sign. Note the remote:boolean
option. This tells ActiveRecord (the DSL language and library that talks to the database for us) that this column should be a boolean
type.
If you don't pass the option at all it's assumed a type of String
in the database.
Running this generator should create a full resource for Job
as well as set a reference to our User
model thanks to the user:references
option. This option is a huge time saver that automatically generates a user_id
column on the jobs
database table. From there, we can assign a given user a job once they create a new one which effectively allows them to "own" that specific job. This will be useful coming up when we want to give users the ability to create, edit, update, and delete a job on their own.
Once the generation is complete you may notice some updates to the app/models/job.rb
file after creation. There should be a new belongs_to :user
association already in place.
We still need to add an additional association to the app/models/user.rb
file but that's a quick and easy task:
# app/models/user.rb
class User < ApplicationRecord
include SimpleDiscussion::ForumUser
has_person_name
has_many :jobs, dependent: :destroy # add this line
...
end
Because a Job
belongs to a User
, we want a user to be able to "own" and manage multiple jobs. The line above allows just that.
The dependent: :destroy
option means that if a User
account is deleted for whatever reason, all the associated jobs will be deleted as well. This is a good practice to keep only the necessary data in your database at all times. You don't always need this option(especially if you want to retain that data) but I think it's a good practice for the most part.
A few view updates
Aside from those major updates, I took a brief moment to update our main views
<!-- app/views/shared/_head.html.erb -->
<% if content_for?(:title) %>
<%= yield :title %> |
<% end %>
railsdevs.com
</title>
<%= csrf_meta_tags %>
<%= stylesheet_link_tag 'application', media: 'all', 'data-turbolinks-track': 'reload' %>
<%= stylesheet_pack_tag 'application', media: 'all', 'data-turbolinks-track': 'reload' %>
<%= javascript_pack_tag 'application', 'data-turbolinks-track': 'reload' %>
<%= javascript_include_tag 'https://js.stripe.com/v3/', 'data-turbolinks-track': 'reload' %>
</head>
Our _head.html.erb
partial now loads the Stripe.js library we'll need later on and we updated the site title to railsdevs.com
.
Inside our app/views/shared/_header.html.erb
file I added a quick access link to our new job resource. We'll change this later on but for now, this makes it easier to navigate to.
<!-- around line 11 -->
<div class="items-center block w-full text-center lg:flex-1 lg:flex lg:text-left">
<div class="lg:flex-grow">
<%= link_to "Basic Link", "#", class: "block mt-4 lg:inline-block lg:mt-0 lg:mr-4 mb-2 lg:mb-0 link" %>
<%= link_to "Jobs", jobs_path, class: "block mt-4 lg:inline-block lg:mt-0 lg:mr-4 mb-2 lg:mb-0 link" %>
</div>
<div class="items-center block w-full mt-2 text-center lg:flex lg:flex-row lg:flex-1 lg:mt-0 lg:text-left lg:justify-end">
<% if user_signed_in? %>
Install action text and active storage
Finally, we installed both action text and active storage in one command:
rails action_text:install
This should fetch the appropriate JavaScript and Ruby dependencies we'll leverage later on.
The series so far
Categories
Collection
Part of the Let's Build for Ruby and Rails Developers collection
Products and courses
-
Hello Hotwire
A course on Hotwire + Ruby on Rails.
-
Hello Rails
A course for newcomers to Ruby on Rails.
-
Rails UI
UI templates and components for Rails.