Skip to content

Commit d9157ba

Browse files
committed
feat: Use FriendlyId in JobPosts to avoid using IDs in URLs
1 parent dad3f52 commit d9157ba

9 files changed

+162
-7
lines changed

Gemfile

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -50,6 +50,7 @@ gem "googleauth", "~> 1.1.0"
5050
gem "dotenv-rails"
5151
gem "ruby-openai"
5252
gem "pdf-reader", "~> 2.11"
53+
gem "friendly_id", "~> 5.5.0"
5354

5455
group :development, :test do
5556
# See https://guides.rubyonrails.org/debugging_rails_applications.html#debugging-with-the-debug-gem

Gemfile.lock

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -134,6 +134,8 @@ GEM
134134
multipart-post (~> 2.0)
135135
faraday-net_http (3.4.0)
136136
net-http (>= 0.5.0)
137+
friendly_id (5.5.1)
138+
activerecord (>= 4.0.0)
137139
fugit (1.11.1)
138140
et-orbi (~> 1, >= 1.2.11)
139141
raabro (~> 1.4)
@@ -497,6 +499,7 @@ DEPENDENCIES
497499
dotenv-rails
498500
factory_bot_rails
499501
faker
502+
friendly_id (~> 5.5.0)
500503
google-api-client (~> 0.53.0)
501504
googleauth (~> 1.1.0)
502505
importmap-rails

app/controllers/employer/google_oauth_controller.rb

Lines changed: 6 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -17,21 +17,22 @@ def callback
1717
update_google_tokens(client.authorization)
1818
return_to = session.delete(:return_to)
1919

20-
if return_to =~ %r{/employer/job_posts/(\d+)/job_applications/(\d+)/interviews}
21-
job_post_id = $1
20+
if return_to =~ %r{/employer/job_posts/([^/]+)/job_applications/(\d+)/interviews}
21+
job_post_slug = $1
2222
job_application_id = $2
23-
job_post = Current.user.organization.job_posts.find(job_post_id)
23+
24+
job_post = Current.user.organization.job_posts.friendly.find(job_post_slug)
2425
job_application = job_post.job_applications.find(job_application_id)
2526
interview = job_application.interviews.last
2627

2728
creator = GoogleCalendar::GoogleCalendarEventCreator.new(interview)
2829
result = creator.create_event
2930

3031
if result == true
31-
redirect_to employer_job_post_job_application_path(job_post_id, job_application_id),
32+
redirect_to employer_job_post_job_application_path(job_post_slug, job_application_id),
3233
notice: "Interview scheduled successfully with Google Calendar!"
3334
else
34-
redirect_to employer_job_post_job_application_path(job_post_id, job_application_id),
35+
redirect_to employer_job_post_job_application_path(job_post_slug, job_application_id),
3536
alert: "Failed to create Google Calendar event. Please try again."
3637
end
3738
else

app/controllers/employer/job_posts_controller.rb

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -16,7 +16,7 @@ def index
1616
end
1717

1818
def show
19-
@job_post = Current.user.organization.job_posts.find(params[:id])
19+
@job_post = Current.user.organization.job_posts.friendly.find(params[:id])
2020
end
2121

2222
def new

app/models/job_post.rb

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,7 @@
11
class JobPost < ApplicationRecord
2+
extend FriendlyId
3+
friendly_id :title, use: :slugged
4+
25
belongs_to :user
36
belongs_to :organization
47
has_many :job_applications, dependent: :destroy

config/initializers/friendly_id.rb

Lines changed: 107 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,107 @@
1+
# FriendlyId Global Configuration
2+
#
3+
# Use this to set up shared configuration options for your entire application.
4+
# Any of the configuration options shown here can also be applied to single
5+
# models by passing arguments to the `friendly_id` class method or defining
6+
# methods in your model.
7+
#
8+
# To learn more, check out the guide:
9+
#
10+
# http://norman.github.io/friendly_id/file.Guide.html
11+
12+
FriendlyId.defaults do |config|
13+
# ## Reserved Words
14+
#
15+
# Some words could conflict with Rails's routes when used as slugs, or are
16+
# undesirable to allow as slugs. Edit this list as needed for your app.
17+
config.use :reserved
18+
19+
config.reserved_words = %w[new edit index session login logout users admin
20+
stylesheets assets javascripts images]
21+
22+
# This adds an option to treat reserved words as conflicts rather than exceptions.
23+
# When there is no good candidate, a UUID will be appended, matching the existing
24+
# conflict behavior.
25+
26+
# config.treat_reserved_as_conflict = true
27+
28+
# ## Friendly Finders
29+
#
30+
# Uncomment this to use friendly finders in all models. By default, if
31+
# you wish to find a record by its friendly id, you must do:
32+
#
33+
# MyModel.friendly.find('foo')
34+
#
35+
# If you uncomment this, you can do:
36+
#
37+
# MyModel.find('foo')
38+
#
39+
# This is significantly more convenient but may not be appropriate for
40+
# all applications, so you must explicitly opt-in to this behavior. You can
41+
# always also configure it on a per-model basis if you prefer.
42+
#
43+
# Something else to consider is that using the :finders addon boosts
44+
# performance because it will avoid Rails-internal code that makes runtime
45+
# calls to `Module.extend`.
46+
#
47+
config.use :finders
48+
#
49+
# ## Slugs
50+
#
51+
# Most applications will use the :slugged module everywhere. If you wish
52+
# to do so, uncomment the following line.
53+
#
54+
config.use :slugged
55+
#
56+
# By default, FriendlyId's :slugged addon expects the slug column to be named
57+
# 'slug', but you can change it if you wish.
58+
#
59+
# config.slug_column = 'slug'
60+
#
61+
# By default, slug has no size limit, but you can change it if you wish.
62+
#
63+
# config.slug_limit = 255
64+
#
65+
# When FriendlyId can not generate a unique ID from your base method, it appends
66+
# a UUID, separated by a single dash. You can configure the character used as the
67+
# separator. If you're upgrading from FriendlyId 4, you may wish to replace this
68+
# with two dashes.
69+
#
70+
# config.sequence_separator = '-'
71+
#
72+
# Note that you must use the :slugged addon **prior** to the line which
73+
# configures the sequence separator, or else FriendlyId will raise an undefined
74+
# method error.
75+
#
76+
# ## Tips and Tricks
77+
#
78+
# ### Controlling when slugs are generated
79+
#
80+
# As of FriendlyId 5.0, new slugs are generated only when the slug field is
81+
# nil, but if you're using a column as your base method can change this
82+
# behavior by overriding the `should_generate_new_friendly_id?` method that
83+
# FriendlyId adds to your model. The change below makes FriendlyId 5.0 behave
84+
# more like 4.0.
85+
# Note: Use(include) Slugged module in the config if using the anonymous module.
86+
# If you have `friendly_id :name, use: slugged` in the model, Slugged module
87+
# is included after the anonymous module defined in the initializer, so it
88+
# overrides the `should_generate_new_friendly_id?` method from the anonymous module.
89+
#
90+
# config.use :slugged
91+
# config.use Module.new {
92+
# def should_generate_new_friendly_id?
93+
# slug.blank? || <your_column_name_here>_changed?
94+
# end
95+
# }
96+
#
97+
# FriendlyId uses Rails's `parameterize` method to generate slugs, but for
98+
# languages that don't use the Roman alphabet, that's not usually sufficient.
99+
# Here we use the Babosa library to transliterate Russian Cyrillic slugs to
100+
# ASCII. If you use this, don't forget to add "babosa" to your Gemfile.
101+
#
102+
# config.use Module.new {
103+
# def normalize_friendly_id(text)
104+
# text.to_slug.normalize! :transliterations => [:russian, :latin]
105+
# end
106+
# }
107+
end
Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,21 @@
1+
MIGRATION_CLASS =
2+
if ActiveRecord::VERSION::MAJOR >= 5
3+
ActiveRecord::Migration["#{ActiveRecord::VERSION::MAJOR}.#{ActiveRecord::VERSION::MINOR}"]
4+
else
5+
ActiveRecord::Migration
6+
end
7+
8+
class CreateFriendlyIdSlugs < MIGRATION_CLASS
9+
def change
10+
create_table :friendly_id_slugs do |t|
11+
t.string :slug, null: false
12+
t.integer :sluggable_id, null: false
13+
t.string :sluggable_type, limit: 50
14+
t.string :scope
15+
t.datetime :created_at
16+
end
17+
add_index :friendly_id_slugs, [:sluggable_type, :sluggable_id]
18+
add_index :friendly_id_slugs, [:slug, :sluggable_type], length: {slug: 140, sluggable_type: 50}
19+
add_index :friendly_id_slugs, [:slug, :sluggable_type, :scope], length: {slug: 70, sluggable_type: 50, scope: 70}, unique: true
20+
end
21+
end
Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,6 @@
1+
class AddSlugToJobPosts < ActiveRecord::Migration[8.0]
2+
def change
3+
add_column :job_posts, :slug, :string
4+
add_index :job_posts, :slug, unique: true
5+
end
6+
end

db/schema.rb

Lines changed: 14 additions & 1 deletion
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

0 commit comments

Comments
 (0)