FriendlyId is the “Swiss Army bulldozer” of slugging and permalink plugins for ActiveRecord. It allows you to create pretty URL’s and work with human-friendly strings as if they were numeric ids for ActiveRecord models.
I'm seeing this issue, when submitting a form for a model object which has validations defined on the base field. I've described my workaround below. I will also be submitting a PR.
Setup:
$ rails new friendly_id_debug
$ cd friendly_id_debug
$ rails g scaffold Post title:string body:text
$ rails db:migrate
In app/models/post.rb:
class Post < ApplicationRecord
validates :title, presence: true
validates :body, presence: true
end
In db/seeds.rb
10.times do |i|
Post.create({
title: "Post #{i + 1}",
body: "words" * 100
})
end
ActiveRecord::RecordNotFound (can't find record with friendly id: "88c1529f-c6d9-4a4a-8b27-45f259091baa")
unit tests:
In test/models/post_test.rb add:
require "test_helper"
class PostTest < ActiveSupport::TestCase
# normal use
test "slug updates when title updates" do
# set up
post = Post.create({
title: "My First Post",
body: "My Deep Thoughts"
})
assert_equal("my-first-post", post.slug)
# execute
post.update(title: "Changed My Mind")
# desired behavior
assert_equal("changed-my-mind", post.slug)
end
end
This should be green already. Add a red test:
# edge case
# validatition bug:
# Expected ["my-second-post", "2d883d62-9c3e-4158-9daf-b34e655dcc3e"] to be nil.
# This bug will break form behavior after an invalid submission
test "slug unchanged when title update is invalid, in :valid? call" do
# set up
post = Post.create({
title: "My Second Post",
body: "More Deep Thoughts"
})
original_slug = post.slug.dup
# execute
post.assign_attributes({ title: "" }) # set .changes
refute(post.valid?) # set .errors; falsy expected; trigger bug
# desired behavior, slug not touched on failed validation for :title
assert_nil(post.changes["slug"])
assert_equal(original_slug, post.slug)
end
I'm seeing this issue, when submitting a form for a model object which has validations defined on the base field. I've described my workaround below. I will also be submitting a PR.
Setup:
In
app/models/post.rb
:In
db/seeds.rb
Finish:
test form validations as normal.
add
friendly_id
:in
app/models/post.rb
:And
app/controllers/posts_controller.rb
:Update existing records:
Add update slug on title change:
In
app/models/post.rb
:Go back to edit, and try submitting with a blank field.
But now open a chrome inpsector and look at the form:
Submitting error:
unit tests:
In
test/models/post_test.rb
add:This should be green already. Add a red test:
debugging:
The fix. in
app/models/post.rb
, add:The
base
field (i.e.:title
)should also be checked for errors.with refactoring:
config in initializer: