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
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.
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
I've been successfully using friendly_id with multiple models in my Rails 4 app. I previously had two models that I've decided to combine (Terminals and Locations). The Terminals show action was previously working with friendly_id, where I could visit /terminals/albany and get the Terminal with the slug 'albany'.
I've gone through the bulk of the refactor to condense down to just Locations, and am on the last piece (fixing views). Along the way I've been testing things in Rails Console and using rspec, and things seem to work as expected. There are other models that still work as expected as well.
Now that I'm working on the new show page for Locations, I'm getting the following error when attempting to visit /locations/albany:
ActiveRecord::RecordNotFound in LocationsController#show
Couldn't find Location with id=albany
However, when I visit /locations/5, it successfully loads the record. It also works in Rails Console:
Location.find(5) # returns the correct location object
Location.find(5).slug # returns 'albany'
Location.friendly.find('albany') # returns the correct location object
When I look at the generated slugs in Rails dbconsole, they all look like they were generated correctly and there are no duplicates.
Here's my model:
Class Location < ActiveRecord::Base
extend FriendlyId
friendly_id :slug_candidates, use: [:slugged, :history]
def slug_candidates
[
:name,
[:name, :zip],
[:name, :zip, :location_type],
]
end
end
And here's my show action in my controller:
def show
#locations = Location.friendly.find(params[:id])
end
I'm stumped! Any hints?
have you tried adding :finders?
friendly_id :slug_candidates, use: [:slugged, :history, :finders]
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)
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