Inside my model, I have the following:
friendly_id :id_and_title, use: [:slugged, :finders]
...
def id_and_title
"#{self.id}-#{self.title}"[0,100]
end
However, when creating a new record, the ID isn't being used on the slug field.
What I'm currently doing is:
after_save :regenerate_slug
...
def regenerate_slug
self.slug = nil
self.save
end
and I'm wondering if there is any other way of doing this?
So the problem is that friendly_id does not have access to "id" until after the record is created. The issue here is the after_save causes infinite recurrence, like Michal said, since it calls save.
You should use an after_create instead. You only need to do this once. In all subsequent updates to the record the id should be available for friendly_id to pick up.
#user3062913 has the solution for it here:
Rails4 Friendly_id Unique Slug Formatting
Related
I have a model, and in accordance with the friendly_id gem it looks like this:
class FinancialYear < ApplicationRecord
extend FriendlyId
friendly_id :slug_candidates, use: :slugged
def slug_candidates
[
:end_year,
[:end_year, :max_id]
]
end
def should_generate_new_friendly_id?
self.slug.blank? || self.year_changed?
end
def end_year
if !self.year.nil? && self.year.length > 1
self.year.split('-')[-1].strip
else
self.year
end
end
def max_id
FinancialYear.where(year: end_year).count + 1
end
end
What it's supposed to do is turn a year:'1999-2000' into a slug: '2000' and 2000-2...etc to avoid collisions.
Unfortunately my tests are failing expected: "2000", got: "2000-f7608e8b-a2e7-449c-ae54-4785c7a68dec"
I am using friendly_id on another model in my app and am using the same technique, and its working perfectly. Any help or suggestions as to why this isn't working would be very appreciated.
UPDATE
After more experimentation I've discovered that this seems to only be happening in my rspec tests - but I don't understand why? Any thoughts?
i had the same issue,i resolved it by deleting all slug values first and then re-running my save.
###delete all slug values
User.update_all(:slug=>nil)
###re-run to get the new slug candidate affective overriding the default alpanumeric slug
User.find_each(&:save)
Hope it helps.
I have Job model and Location model in my rails application. I am using postgresql as a database.so i have location_ids as an array field in my Job model for holding locations. I am using FeriendlyId in my application to make my url friendly. when i go to my job show page i am getting this friendly url
http://localhost:3000/jobs/seo-trainee
but now i also want to include the locations the job has in my url , something like this
http://localhost:3000/jobs/seo-trainee-mumbai-tokyo
i know we can use slug_candidates for this purpose. but i dont know how can i achieve this exactly
currently i have this in my Job model
extend FriendlyId
friendly_id :slug_candidates, use: [:slugged, :finders]
def slug_candidates
[
:title,
[:title, :id]
]
end
You need to define a custom method to generate your slug definition, and then tell FriendlyId to use that method.
The documentation gives this example:
class Person < ActiveRecord::Base
friendly_id :name_and_location
def name_and_location
"#{name} from #{location}"
end
end
bob = Person.create! :name => "Bob Smith", :location => "New York City"
bob.friendly_id #=> "bob-smith-from-new-york-city"
So in your case, you would use something like this:
class SomeClass
friendly_id :job_name_and_location
def job_name_and_location
"#{name} #{locations.map(&:name).join(' ')}"
end
end
I've made a few assumptions:
Your job model has a name attribute (seo training)
Your job model has_many locations, each of which have a name attribute
We then create a method which defines the non-friendly string which FriendlyId will use to create a slug from. In this case it'll come up with something like SEO Training Mumbai Tokyo and use that to create your seo-training-mumbai-tokyo slug.
You can use something like the following:
extend FriendlyId
friendly_id :slug_candidates, use: [:slugged, :finders]
def slug_candidates
locs = Location.where("id IN(?)", self.location_ids).collect{|l| l.name}.join("-") // here, we find the locations for the current job, then joins the each locations name with a '-' sign
return self.title+"-"+locs // here, returns the job title with the location names
end
So, if your current Job holds location_ids = [1,2,3]
then from Location table we find the locations with id = 1,2,3. Then join their names.
I'm using the following:
gem 'friendly_id', github: 'FriendlyId/friendly_id', branch: 'master'
I am creating an Article section on my Rails 4 website. The problem I am having is that when I change a existing article's name the slug is not updated.
This is what I have so far:
extend FriendlyId
friendly_id :name, use: :slugged
add_column :articles, :slug, :string
add_index :articles, :slug, unique: true
In FriendlyId 4 (Rails 3 compatible) there was a method
should_generate_new_friendly_id?
and you could define it on your model to control when slug is regenerated.
Try
def should_generate_new_friendly_id?
name_changed?
end
to regenerate slug when name changes.
EDIT
FriendlyId version 5 (Rails 4 compatible) doesn't regenerate slugs on save anymore. To restore this functionality you can either set slug column to nil before saving or use the solution provided above.
EDIT 2
You need to override the slug setter for your saves to work for Rails <5 & FriendlyId > 5 as referenced in this issue.
Add this to the model file
def slug=(value)
if value.present?
write_attribute(:slug, value)
end
end
I have this issues and just want to point out what I've noticed.
if you only do as in docs
class Post < ActiveRecord::Base
extend FriendlyId
friendly_id :title, use: :slugged
end
and then run Post.find_each(&:save) - slug is gonna get updated...
However in my case, I also have these in my model
class Post < ActiveRecord::Base
extend FriendlyId
friendly_id :title, use: :slugged
def normalize_friendly_id(text)
text.to_slug.normalize(transliterations: :russian).to_s
end
def should_generate_new_friendly_id?
title_changed?
end
end
with the code above it won't do anything when you run Post.find_each(&:save) I assume since your title doesn't change. (first method handles russian language)
so when working with the first model all worked great, but then when I copied ready code to next model I wanted to slugify, I run into some issues. Hope it helps someone.
I was facing a similar issue where the slug don't want to change on update even though I had the following in my model:
def should_generate_new_friendly_id?
slug.blank? || title_changed?
end
In my case I wasn't setting the title directly via the form but in a before_save callback, and that was the reason, because it seems that FriendlyID needs the change to happen not only before saving but even before_validation so I changed my code to:
before_validation :set_title
def set_title
self.title = 'some dynamic way of getting title'
end
And, that worked for me!
I really spent too much time trying to figure out why it wasn't working, so I'm posting this case here for anyone who may get stuck in the same situation (and hopefully save you long hours of debugging)
I just added the Friendly_id gem to my rails project looking to not use the database id and create a slug, but I can't seem to create slugs for old records. My model looks like this.
class Mapping < ActiveRecord::Base
extend FriendlyId
friendly_id :title, use: :slugged
# Friendly_Id code to only update the url for new records
def should_generate_new_friendly_id?
new_record? || slug.blank?
end
end
I then am running Model.find_each(&:save) but it keeps spitting out a nil result. I've tried commenting out the should_generate_new_friendly_id completely but with no luck. Anyone see what I'm doing wrong here?
EDIT
I rolled my database back and rewrote my migratations and that has appeared to have fixed the issue.
If anyone finds this 6 year old question:
Without a "do and end" block:
find_each returns an Enumerable (not an ActiveRecord Class)
Use:
Model.find_each.select(&:save)
Notice the added "select" method
Doing stuff with Rails’ find_each
Trying to get my app running the FriendlyId gem (version 4.0.1)
I think I'm doing this in the wrong order, but I want to strip out apostrophes before my friendly_id slug is generated when creating a new record. But I think the method normalize_friend_id is being called after the id is already generated.
I've added the following to my model:
class Team < ActiveRecord::Base
extend FriendlyId
friendly_id :name, :use => :slugged
def normalize_friendly_id(string)
super.gsub("\'", "")
end
end
super calls the superclass first, meaning the friendly id is being generated then you're running gsub on THAT result. What you really want is to override this method completely.
Refer to: https://github.com/norman/friendly_id/blob/master/lib/friendly_id/slugged.rb#L244-246
your code should look like this:
def normalize_friendly_id(string)
string.to_s.gsub("\'", "").parameterize
end
or
def normalize_friendly_id(string)
super(string.to_s.gsub("\'", ""))
end
Hope that helps