First off I'm very new to rails and ruby for that matter.
I'm trying to create a slug for custom URLS.
I have a user model with a first_name & last_name. This model also has a slug.
What I am trying to do is get it to check my database to see if there is an instance of the slug to be created eg. see how many Joe Blogs exist. If there is none it gets the url whatever.com/joe-blogs and if there more than 0 it gets the value of the count whatever.com/joe-blogs-COUNT
what I attempted to do when gerating my slug
def generate_slug
NoSlug == Users.sum("slug").where(:slug => first_name-last_name.perameterize)
if NoSlug == 0
self.slug ||= first_name-last_name.perameterize
else NoSlug > 0
self.slug ||= first_name-last_name-NoSlug.perameterize
end
end
I've tried to fix it but with no such luck - Any suggestions or pointers?
Thanks!
I have been using the FriendlyId gem and have been pleased. If you want to combine first name and last name, you could do something like
class User < ActiveRecord::Base
extend FriendlyId
friendly_id :full_name, use: :slugged
def full_name
"#{first_name} #{last_name}"
end
end
The gem will take care of parameterizing it for you. This RailsCast might also be helpful.
write these methods in model
before_validation :generate_slug
def name
"#{first_name} #{last_name}"
end
def generate_slug
self.slug = self.name.downcase.gsub(/\s+/, '-').gsub(/[^a-zA-Z0-9_]+/, '')
end
Related
I am very new to friendly_id and it matches my need to provide friendly URLs 👍
I have a Group model (i.e. a group of users) for which I generate a unique code upon creation. FYI, this code attribute is set as a unique index in my PostgreSQL database.
class Group < ApplicationRecord
include FriendlyId
friendly_id :code
after_create :generate_group_code
private
def normalize_friendly_id(value)
super.upcase
end
def generate_group_code(size = 8)
allowed_chars = ("a".."z").to_a
code = (1..size).map { allowed_chars[rand(26)] }.join while Group.exists?(code: code)
update(code: code)
end
end
I think I have followed the gem's guide properly, I just want the generated code to be upcased in the URLs (i.e. /group/ABCDEFGH).
The friendly_id is indeed set as my code attribute, but it is not upcased. I placed a byebug in the normalize_friendly_id method but it is never triggered. What am I missing?
The normalize_friendly_id is only called when using the slugged module to use a slug column to store and find by this column:
friendly_id :code, use: :slugged
Using this you can then override the normalize_friendly_id method.
Sunny's way is probably the way to go in general, as the slugged module is required to edit internal methods such as normalize_friendly_id.
In my case, I already have a code attribute that is unique. Using the slugged module would create a new attribute called slug, which would be exactly the same as my code. I want to avoid that duplication.
In the end, I decided to dodge the friendly_id gem and directly override the to_param method the my model (inspired by this gist):
class Group < ApplicationRecord
validates :code, format: { with: /\A[a-z]{8}\z/ }, uniqueness: true, on: :update
after_create :generate_group_code
# Override the method to allow '/group/MYGROUPA' paths
def to_param
code.upcase
end
# A group code is made of letters exclusively.
# Converting a String (or nil) to an integer leads to 0.
# In this case, lookup by code, otherwise, lookup by id.
# Note the 'downcase', which allows URLs to be case insensitive.
def self.find(input)
input.to_i == 0 ? find_by_code!(input.downcase) : super
end
private
def generate_group_code(size = 8)
allowed_chars = ("a".."z").to_a
code = (1..size).map { allowed_chars[rand(26)] }.join while Group.exists?(code: code)
update(code: code)
end
end
I'll edit this answer if I encounter any side effect, but for now it works.
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.
When I GET /admin/consoles/1/edit, for instance, this happens:
Couldn't find Brand with 'id'=
And then it is highlighting the following code fragment which I have in
/app/models/console.rb:
def full_name
brand = Brand.find(self.brand_id).name
"#{brand} #{self.name}"
end
Seems like it isn't recognizing self.brand_id. Ideas?
I would need to see your app/models/console.rb to be sure, but it seems that you should have a belongs_to relation and then you could just use that relation ...like this:
class Console < ActiveRecord::Base
belongs_to :brand
def full_name
"#{brand.name} #{name}"
end
end
But maybe you should have something guarding that like this:
def full_name
("#{brand.name} " if brand.present?) << "#{name}"
end
You could avoid the error with a test for the presence of brand_id parameter:
def full_name
if self.brand_id.present?
brand = Brand.find(self.brand_id).name
"#{brand} #{self.name}"
else
self.name #or other combination of parameters not using the brand model
end
end
Let us know if this helps you.
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