Let's build for Ruby and Rails developers - Part 5
Tags: rails, ruby, tutorial, video
Published: 2020-08-15 19:54:55

---
title: Let's build for Ruby and Rails developers - Part 5
published: true
date: 2020-08-07 15:21:00 UTC
tags: rails, ruby, tutorial, video
canonical_url: https://web-crunch.com/posts/lets-build-for-ruby-and-rails-developers-part-5
---

{% youtube nzKkYIdySEs %}

### 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:

```ruby
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).

```ruby
Job::JOB_STATUSES[:published] # returns "published"

```

### Leveraging friendly\_id

Thanks to my [kickoff\_tailwind template](https://github.com/justalever/kickoff_tailwind) we used to kick off the app originally I'm able to easily configure the [friendly\_id gem](https://github.com/norman/friendly_id/). For friendly\_id to work we needed a new `slug` field on the `posts` database table so that was added in this part

```bash
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

```bash
rails db:drop
rails db:create
rails db:migrate
rails db:seed
```

Or alternatively you could run:

```bash
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:

```ruby
User.delete_all
Job.delete_all

admin = User.new(email: "andy@web-crunch.com", password: "password", password_confirmation: "password", admin: true, developer: true, employer: true)
admin.skip_confirmation!
admin.save

developer = User.new(email: "developer@web-crunch.com", password: "password", password_confirmation: "password", admin: false, developer: true, employer: false)
developer.skip_confirmation!
developer.save

employer = User.new(email: "employer@web-crunch.com", 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

- [Part 1](https://dev.to/justalever/let-s-build-for-ruby-and-rails-developers-2g1)
- [Part 2](https://dev.to/justalever/let-s-build-for-ruby-and-rails-developers-part-2-3310)
- [Part 3](https://web-crunch.com/admin/posts/lets-build-for-ruby-and-rails-developers-part-3/)
- [Part 4](https://web-crunch.com/admin/posts/lets-build-for-ruby-and-rails-developers-part-4/)
- Part 5 (You are here)