I've been following the Railscast tutorial on how to implement friendly_id and for some reason my URL's doesn't change after I update my attributes.
Say I registered a user with :fullname 'John Doe' it creates the slug /john-doe successfully. However if I update my name to 'Test user' the url is still /john-doe
My current setup is this:
users_controller
def show
#user = User.friendly.find(params[:id])
end
model - user.rb
extend FriendlyId
friendly_id :fullname, use: [:slugged, :history]
I've also migrated
class AddSlugsToUsers < ActiveRecord::Migration
def change
add_column :users, :slug, :string
add_index :users, :slug, unique: true
end
end
so that is working. Also installed
rails generate friendly_id
and done:
User.find_each(&:save)
in rails c
What am I doing wrong?
friendly_id
It's a common issue with friendly_id - it defaults to setting the slug only if the slug attribute is blank:
Documentation
As of FriendlyId 5.0, slugs are only generated when the slug field is
nil. If you want a slug to be regenerated,set the slug field to nil:
restaurant.friendly_id # joes-diner
restaurant.name = "The Plaza Diner"
restaurant.save!
restaurant.friendly_id # joes-diner
restaurant.slug = nil
restaurant.save!
restaurant.friendly_id # the-plaza-diner
You can also override the #should_generate_new_friendly_id? method, which lets you control exactly when new friendly ids are set:
class Post < ActiveRecord::Base
extend FriendlyId
friendly_id :title, :use => :slugged
def should_generate_new_friendly_id?
title_changed?
end
end
If you want to extend the default behavior but, adding your own conditions, don't forget to invoke super from your implementation:
class Category < ActiveRecord::Base
extend FriendlyId
friendly_id :name, :use => :slugged
def should_generate_new_friendly_id?
name_changed? || super
end
end
For you, I'd recommend:
#app/models/user.rb
Class User < ActiveRecord::Base
...
def should_generate_new_friendly_id?
name_changed? || super
end
end
Related
I'm using the very useful option:
# config/initializers/friendly_id.rb
# ...
config.use Module.new {
def should_generate_new_friendly_id?
slug.blank?
end
}
The issue is that I use friendly_id on 3 or 4 different models for which the attribute to watch for a change is differrent. For some it's name, others title. Is there a way to dynamically retrieve the attribute symbol that was passed to friendly_id :title, use: :slugged to prevent this being necessary in models?
# app/models/foo.rb
class Foo < ActiveRecord::Base
# ...
friendly_id :title, use: :slugged
def should_generate_new_friendly_id?
super || title_changed?
end
end
```
Ideally I'm looking for something like:
# config/initializers/friendly_id.rb
# ...
config.use Module.new {
def should_generate_new_friendly_id?
slug.blank? || friendly_id_attribute_changed?
end
}
Can this be achieved?
I'm using FriendlyID to make my URLs look nice, so what I got now is:
/items/my-personal-item
But what I would like is:
/user1/my-personal-item
Where user1 is the username of a user owning my-personal-item.
#config/routes.rb
resources :users, path: "" do
resources :items, path: "", only: [:show, :index] #-> url.com/:user_id/
end
FriendlyID has to reside in any model with which you're using, so if you wanted to use it on User as well as Item, you'd have to use the following setup:
#app/models/user.rb
class User < ActiveRecord::Base
extend FriendlyId
friendly_id :username
end
#app/models/item.rb
class Item < ActiveRecord::Base
extend FriendlyId
friendly_id :title
end
This should give you the ability to call: url.com/user-name/my-personal-item
--
If you wanted to back it all up with the appropriate controller actions, you could use the following:
#app/controllers/items_controller.rb
class ItemsController < ApplicationController
def show
#user = User.find params[:user_id]
#item = #user.items.find params[:id]
end
end
As a pro-tip, you'll want to look at the friendly_id initializer to set some defaults for your app:
#config/initializers/friendly_id.rb
config.use :slugged #-> make sure this is valid
config.use :finders #-> make sure this is valid
By setting the above options, you'll just be able to call friendly_id [[attribute]] in your models.
FriendlyID also supports Unique Slugs by Scope.
Because you haven't provided some information about your app I can't give you a exact explanation.
But I've created an explanation based on your url.
If I'm right than you have a Item and a User model. The Item model belongs to the User model and the User model has many Items.
Here is how I would solve this problem:
Models:
# User model
class User < ActiveRecord::Base
extend FriendlyId
has_many :items
friendly_id :name, :use => :slugged
end
# Item model
class Item < ActiveRecord::base
extend FriendlyId
belongs_to :user
friendly_id :name, :use => :scoped, :scope => :user
end
Controller:
class ItemsController < ApplicationController
before_action :set_user, :set_item
def show
# Show your item ...
end
private
def set_user
User.friendly.find(params[:user_id])
end
def set_item
#user.items.friendly.find(params[:item_id])
end
end
Routes:
Rails.application.routes.draw do
get ':user_id/:item_id' => 'items#show'
end
This is a minimal implementation without validation. I hope this helps.
Happy coding :)
I have tried many solution and came up with good one but still getting error. I am editing my whole question.
I am trying to create Friendly URL with friendly_id gem.
In my project First user need to signup with devise.
Devise will pass some information to profile model with
model/user.rb
delegate :age, :city, :region, :country, to: :profile
I want to make user.name to be Friendly_id candidate. I have tried following code in my Profile model:-
class Profile < ActiveRecord::Base
extend FriendlyId
friendly_id :user_name , use: :slugged
def user_name
user.name
end
But it is giving error
NoMethodError at /
undefined method `name' for nil:NilClass
now After submitting user form.
Please suggest possible solution with explanation.
My User.rb looks like
require 'open-uri'
class User < ActiveRecord::Base
paginates_per 10
validates :name , presence: true, length: { maximum: 200 }
scope :by_name, ->(name) do
joins(:profile).where('lower(name) LIKE ?', "%#{name.downcase}%")
end
delegate :age, :city, :region, :country, to: :profile
has_one :profile, dependent: :destroy
accepts_nested_attributes_for :profile
def self.new_with_session(params, session)
session_params = { 'profile_attributes' => {} }
provider = session['devise.provider']
if provider && data = session["devise.#{provider}"]
session_params['name'] = data[:name] if data[:name]
session_params['email'] = data[:email] if data[:email]
session_params['profile_attributes'] =
{ avatar: data[:image] } if data[:image]
end
params.deep_merge!(session_params)
super.tap do |user|
if auth = Authorization.find_from_session(session, provider)
user.authorizations << auth
end
end
end
def password_required?
super && registered_manually?
end
def registered_manually?
encrypted_password.present?
end
end
And my profile.rb looks like
class Profile < ActiveRecord::Base
extend FriendlyId
friendly_id user.name, use: :slugged
belongs_to :user
accepts_nested_attributes_for :user
validates :website, allow_blank: true, uri: true
def website=(url_str)
if url_str.present?
url_str = "http://#{url_str}" unless url_str[/^https?/]
write_attribute :website, url_str
end
end
end
I think Problem is here:
Request parameters
{"action"=>"new", "controller"=>"users/registrations"}
Please suggest possible solution and explanation.
And users/registration:
class Users::RegistrationsController < Devise::RegistrationsController
layout 'land'
def create
params[:user][:profile_attributes].delete(:place)
end
protected
def after_sign_up_path_for(resource)
welcome_path
end
end
I am creating user in profile controller
def load_profile
#profile = Profile.friendly.find(params[:id])
if !#profile || #profile.user.blocked_users.include?(current_user)
redirect_to home_path
else
#user = #profile.user
end
end
#Rodrigo helped me find out error that error is due to Friendly_id can't create link with user instance.
There is an error on this line:
friendly_id user.name, use: :slugged
The variable user doesn't exists at Profile class scope. You should use something like this:
friendly_id :user_name, use: :slugged
def user_name
user.name
end
extend FriendlyId
friendly_id u_name, use: :slugged
def u_name
user.name
end
belongs_to :user
Have you defined user? what is user.name?
rails newbie here I am using friendly_id gem, I have a Page model when I create page a friendly_id slug generate from title given in text field,
What i want is to able to edit that slug and do not change on update or when i change the title
here is my Page.rb
class Page < ActiveRecord::Base
belongs_to :user
include Bootsy::Container
extend FriendlyId
friendly_id :title, use: :slugged
validates :title, :presence => true
validates :user_id, :presence => true
def should_generate_new_friendly_id?
end
end
I know this is a very basic task but I am new in rails. thanks
Done this by adding the following login inside should_generate_new_friendly_id? :
def should_generate_new_friendly_id?
new_record? || !self.slug.present?
end
I'm using the gem Friendly ID, and I currently have to do 2 saves in order to save the slug.
It doesn't seems right to me.
class Contractor < ActiveRecord::Base
include FriendlyId
friendly_id :slug_candidates, use: :slugged, slug_column: :alias
after_commit :generate_new_alias, unless: Proc.new {|contractor| contractor.business_name_changed? }
def slug_candidates
[
:business_name,
[:business_name, :city],
[:business_name, :city, :state]
]
end
def generate_new_alias
if self.alias != self.alias_was
self.alias = nil
self.save
end
end
end
Any idea what I'm doing wrong ?