History creation timing
With Rails 8
class Book < ActiveRecord::Base
has_many :chapters
include FriendlyId
friendly_id :title , :use => [:slugged, :history]
end
class Chapter < ActiveRecord::Base
include FriendlyId
friendly_id :title , :use => [:slugged, :history]
end
book = Book.new(title: "A book")
book.chapters << Chapter.new(title: "A Chapter")
book.chapters << Chapter.new(title: "A Chapter")
book.save!
Give me
ActiveRecord::RecordNotUnique
According to
# lib/friendly_id/history.rb
72: def self.included(model_class)
73: model_class.class_eval do
74: has_many :slugs, -> { order(id: :desc) }, **{
75: as: :sluggable,
76: dependent: @friendly_id_config.dependent_value,
77: class_name: Slug.to_s
78: }
79:
80: after_save :create_slug
81: end
82: end
the history record is created on a after_save callback instead of around_save callback.
Is it a bug or a feature?
You're trying to create two chapters with the same title and save them at the same time, so the second chapter would raise title not unique, is that unexpected? Do you expect the second slug to have a suffix on it?
Thanks!
Yes, I was expecting the second slug to have a suffix on it. It was my understanding of the uniqueness feature (https://norman.github.io/friendly_id/file.Guide.html#Uniqueness). But, I could have misunderstand the doc.
As an experiment, does this work:
class Book < ActiveRecord::Base
has_many :chapters
include FriendlyId
friendly_id :title , :use => [:slugged, :history]
end
class Chapter < ActiveRecord::Base
include FriendlyId
friendly_id :title , :use => [:slugged, :history]
end
book = Book.new(title: "A book")
book.chapters << Chapter.new(title: "A Chapter")
book.save!
book.chapters << Chapter.new(title: "A Chapter")
book.save!
just to narrow down the issue
Yes, it work if I slip into 2 saves
Adding around_create callback fix the issue.
class Chapter < ActiveRecord::Base
include FriendlyId
friendly_id :title , :use => [:slugged, :history]
around_create :fix_friendly_id_with_history
def fix_friendly_id_with_history
yield
create_slug
end
end
The after_save seems to be call too far in the process.
Thanks for confirming! Seems like maybe we should replace our after_save hook with an around_save here: https://github.com/norman/friendly_id/blob/40b899a547624a50dbe44c5f337ef393e04a9152/lib/friendly_id/history.rb#L80
what do you think?
After some more thought, I think that you should keep after_save and add around_create instead of around_save.
If I understand correctly around_save return from the yield after all the associations of the object are save, while around_create return from yield before the associations of the object are save.
I over simplify the pseudo code to get the error. Sorry. It had to be a kind of linked list where next nodes hade the same slugs.
class Book < ActiveRecord::Base
has_many :chapters
include FriendlyId
friendly_id :title , :use => [:slugged, :history]
end
class Chapter < ActiveRecord::Base
belongs_to :book
belongs_to :next, class_name: "Chapter"
include FriendlyId
friendly_id :title , :use => [:slugged, :history]
end
book = Book.new(title: "A book")
book.chapters.build(title: "A chapter")
book.chapters.build(title: "A chapter")
book.chapters[0].next = book.chapters[1]
book.save!
I make tests with around_create and after_create. It only works with around_create.
If I understand correctly around_save return from the yield after all the associations of the object are save, while around_create return from yield before the associations of the object are save.
Good info, thanks! But I'm not sure that we can use around_create because then when the record is updated we wouldn't re-evaluate any slugs?
If you're interested in spending the time on it, opening a pull request with a failing test for the use case you're aiming for (it's OK if it just a test that fails) would be very helpful in terms of identifying a good solution for this?