August 7, 2020
•Last updated November 5, 2023
Let's build for Ruby and Rails developers - Part 5
Setting up constants and seeded data
Welcome to part 5 of my Let's build for Ruby and Rails developers series. This part will focus on getting our app set up for success using constants and seeded data.
Constants
I really love the pattern of setting up constants in ruby or a given Ruby on rails app. Doing this is pretty mundane but it allows you to access a specific ruby class and get attributes back based on naming conventions you author.
Because our job form is going to be pretty complex I want to have a source of truth for field values and labels. Without these things could get messy as we try to remain as consistent as possible.
My job.rb
model file grows dramatically in this part but most of the values are static.
Here's the final job.rb
file so far:
class Job < ApplicationRecord
extend FriendlyId
friendly_id :title, use: :slugged
belongs_to :user
has_rich_text :description
has_rich_text :company_description
has_one_attached :company_logo
COMPENSATION_TYPES = [
"Contract",
"Full-time"
]
COMPENSATION_RANGES = [
"50,000 - 60,000",
"60,000 - 70,000",
"70,000 - 80,000",
"80,000 - 90,000",
"90,000 - 100,000",
"110,000 - 120,000",
"120,000 - 130,000",
"130,000 - 140,000",
"140,000 - 150,000",
"160,000 - 170,000",
"170,000 - 180,000",
"180,000 - 190,000",
"190,000 - 200,000",
"200,000 - 210,000",
"210,000 - 220,000",
"220,000 - 230,000",
"230,000 - 240,000",
"240,000 - 250,000",
"greater than 250,000",
].freeze
HOURLY_RANGES = [
"less than 10",
"10-30",
"30-60",
"60-90",
"more than 100",
].freeze
JOB_STATUSES = {
pending: "pending",
published: "published",
archived: "archived"
}.freeze
YEARS_OF_EXPERIENCE_RANGE = ["1","2","3","4","5","6","8","9","10","more than 10"].freeze
def pending?
self.status == Job::JOB_STATUSES[:pending]
end
def published?
self.status == Job::JOB_STATUSES[:published]
end
def published?
self.status == Job::JOB_STATUSES[:archived]
end
def should_generate_new_friendly_id?
if !slug?
title_changed?
else
false
end
end
end
Ruby constants have a convention of typically being all caps. This signifies their values shouldn't change. To prevent change you might notice the .freeze
method. This in theory locks the hash or array in place and would return an error should we try to modify a constant.
In future parts, we'll be making use of these values across the app. You can access them via the Job
class. So something like the following would return the associated value(s).
Job::JOB_STATUSES[:published] # returns "published"
Leveraging friendly_id
Thanks to my kickoff_tailwind template we used to kick off the app originally I'm able to easily configure the friendly_id gem. For friendly_id to work we needed a new slug
field on the posts
database table so that was added in this part
rails g migration add_slug_to_posts slug:uniq
Here we extending the slug
field to also leverage a uniq
constraint which adds an additional index to the database. This is necessary so that if you happen to have two jobs with the same name, the gem would identify the duplicate and extend the slug with a hash so they are unique by default.
We also added a new method towards the bottom of the class called should_generate_new_friendly_id?
. This gets called when a job title field is changed. Assuming you change the title
, this method would proceed to also update the slug
field to reflect.
Seeding data
To save a load of time and headache I'm a firm believer in making as much seeded data as possible. In the early stages of a Rails app, I tend to drop and create my database all the time. This is because I'm usually figuring out what data should stay or go on a typical table. Seeding makes the process way easier and less manual since you run a basic command to get data to work with immediately.
I'll often run these commands so damn much lol
rails db:drop
rails db:create
rails db:migrate
rails db:seed
Or alternatively you could run:
rails db:setup
and it does the four commands above. I've run into issues with performance going that route though so take it with a grain of salt.
At the end of this part my db/seeds.rb
file looks like the following:
User.delete_all
Job.delete_all
admin = User.new(email: "[email protected]", password: "password", password_confirmation: "password", admin: true, developer: true, employer: true)
admin.skip_confirmation!
admin.save
developer = User.new(email: "[email protected]", password: "password", password_confirmation: "password", admin: false, developer: true, employer: false)
developer.skip_confirmation!
developer.save
employer = User.new(email: "[email protected]", password: "password", password_confirmation: "password", admin: false, developer: false, employer: true)
employer.skip_confirmation!
employer.save
Job.create!(
id: 1,
company_name: "Google",
company_website: "https://google.com",
compensation_range: "170,000 - 180,000",
compensation_type: "Full-time",
estimated_hours: nil,
featured: false,
featured_until: nil,
headquarters: "California",
link_to_apply: "https://google.com/apply",
price: 199,
published_at: DateTime.now,
remote: false,
slug: "rails-developer-at-google",
status: "published",
title: "Rails developer at Google",
upsell_type: nil,
years_of_experience: "5",
user_id: admin.id,
description: Faker::Hipster.paragraph,
company_description: Faker::Hipster.paragraph
)
Job.create!(
id: 2,
company_name: "Dropbox",
company_website: "https://dropbox.com",
compensation_range: nil,
compensation_type: "Contract",
estimated_hours: "more than 100",
featured: true,
featured_until: 1.week.from_now.beginning_of_day,
headquarters: "California",
link_to_apply: "https://dropbox.com/apply",
price: 299,
published_at: DateTime.now,
remote: true,
slug: "ruby-developer-at-dropbox",
status: "published",
title: "Ruby developer at Dropbox",
upsell_type: "best",
years_of_experience: "5",
user_id: employer.id,
description: Faker::Hipster.paragraph,
company_description: Faker::Hipster.paragraph
)
Job.create!(
id: 3,
company_name: "Apple",
company_website: "https://apple.com",
compensation_range: "240,000 - 250,000",
compensation_type: "Full-time",
estimated_hours: nil,
featured: false,
featured_until: nil,
headquarters: "California",
link_to_apply: "https://apple.com/apply",
price: 199,
published_at: DateTime.now,
remote: false,
slug: "ruby-developer-at-apple",
status: "published",
title: "Ruby developer at Apple",
upsell_type: nil,
years_of_experience: "8",
user_id: employer.id,
description: Faker::Hipster.paragraph,
company_description: Faker::Hipster.paragraph
)
This is a good start at creating some data to work with. This app will have three different user roles admin
, developer
, and employer
that all still leverage the core User
model. We'll simply toggle a few boolean columns where it makes sense per user. I chose this route for its reduced complexity whereas we could have made all new Employer
and Developer
models as well.
Coming up next
There is still so much to do but I hope you have enjoyed the journey so far!
This early content/process isn't the most exciting but to build an app this stuff needs to be accounted for. I'm excited to see where it heads!
Coming up, I plan to start thinking about the layout and design of the app. We'll be leveraging Tailwind CSS for that.
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.