Andy from Webcrunch

Subscribe for email updates:

Portrait of Andy Leverenz
Andy Leverenz

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

Link this article
Est. reading time: 6 minutes
Stats: 1,171 views

Categories

Collection

Part of the Let's Build for Ruby and Rails Developers collection

Products and courses