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.
The core models we'll leverage in this app, to begin with, will be the
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.
User model ends up with a few sets of boolean columns including:
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
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.
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>
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
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
Job belongs to a
User, we want a user to be able to "own" and manage multiple jobs. The line above allows just that.
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.
Aside from those major updates, I took a brief moment to update our main views
_head.html.erb partial now loads the Stripe.js library we'll need later on and we updated the site title to
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? %>
Finally, we installed both action text and active storage in one command: