Okay so I'm working on making a friendly_id that allows me to scale it effectively. This is what I got so far. What it does is essentially add +1 on a count if you have a matching name. So if you are the 2nd person to register as John doe your url would be /john-doe-1.
extend FriendlyId
friendly_id :name_and_maybe_count, use: [:slugged, :history]
def name_and_maybe_count
number = User.where(name: name).count
return name if number == 0
return "#{name}-#{number}"
end
However this piece of code is still bringing me some issues.
If I have created a user called John Doe. Then register another user called John Doe the slug will be /john-doe-UUID. If I register a third user then it will receive the slug john-doe-1.
If I have two users. One that registered with the name first. Say Juan Pablo. Then he changes his name to 'Rodrigo', and then change it back to 'Juan Pablo'. His new slug for his original name will be 'juan-pablo-1-UUID'.
I know this is minor nitpick for most of you but it's something that I need to fix!
You want to overwrite to_param, include the id as the first bit of the friendly id
I extend active record like this in one of my apps,
module ActiveRecord
class Base
def self.uses_slug(attrib = :name)
define_method(:to_param) do
"#{self.id}-#{self.send(attrib).parameterize}"
end
end
end
so I can do
uses_slug :name
in any model
but this alone in any model should work:
def to_param
"#{self.id}-#{self.my_attrib.parameterize}"
end
The benefit of this is that in the controller you when you do
Model.find(params[:id])
you don't have to change anything because when rails tries to convert a string like "11-my-cool-friendly-id" it will just take the first numeric bits and leave the rest.
Related
I have a simple model for students but I want to also have unique student code for each student, I know I can use the student_id but I would like student code to be something like 'STU0001', 'STU0002. How can I implement something like this so that whenever I create a student, it will create the student code and add it to the DB?
Also how can I search students by their ids in this case?
You can do it using before-create hook in your model, something like follow -
class Student < ActiveRecord::Base
# Callbacks
before_create :set_student_id
def set_student_id
self.student_id = "STU" + "%04d" % self.id
end
end
output -
$> student = Student.create()
$> puts student.student_id
$> STU0001
IMHO if you want id to be prefixed as STU just for a nice display purpose then I would suggest not to store that way in database.
Let current implementation of auto incrementing id be untouched.
Just while displaying you can prepend it with STU.
For handy use you can create method to wrap this logic as
def formatted_id # OR friendly_id or whatever you like
"STU#{'%04d' % self.id}"
end
Reasons for not storing prefixed id:
You will have to write a setter
Your column data type will change to string
Your performance will go down as compared to integer column
In future if you decide to change the prefix, then you will have to update existing ids. With my solution you can change all (existing and future) ids formatting anytime by just changing formatting in the method.
Searching depends on exactly what is your usecase. If you can explain that then I can guide you better.
To search students you can write a handy method like below in model (i assume it will be Student or User)
def self.find_by_formatted_id(formatted_id)
self.find(formatted_id.gsub(/^STU/, ''))
end
def self.find_by_formatted_ids(formatted_ids) # formatted_ids will be an array
self.where(id: formatted_ids.map{ |fid| fid.gsub(/^STU/, '') })
end
I'm new to rails and currently involved in an internship and I was assigned to use the friendly_id gem for my tournament class, this is part of the code in it:
class Tournament < ApplicationRecord
extend FriendlyId
friendly_id :url_id
...
end
I don't use a slug since I have a url_id attribute that stores my desired url and when I try with the old .../tournaments/1 everything's all good but with .../tournaments/example I get "example is not a valid value for id" with code 103, status 400. Any ideas what the problem might be?
You have to update your controller for Tournaments so that it uses friendly.find method instead of the find.
# Change this:
Tournament.find(params[:id])
# to
Tournament.friendly.find(params[:id])
When my new website launches there will be very few users. Currently a user's profile page is /users/:id so in the early stages it will be /users/6, etc. I don't want others to know how many users the website has. I think that an long id (such as a uuid) with numbers and letters looks ugly so I would prefer it just be numbers.
How can I use friendly_id gem to create a random number slug that will also be unique? This is also my first website so any "best practices" tips regarding user id obfuscation would be helpful. Thanks.
This is an example from existing project:
class User < ActiveRecord::Base
extend FriendlyId
friendly_id :email, use: [:slugged, :finders]
def normalize_friendly_id(email)
Digest::MD5.hexdigest(email)
end
def should_generate_new_friendly_id?
slug.blank?
end
end
You can change the MD5 to a random number using something like this:
SecureRandom.random_number(1_000_000_000)
I added a new field in devise called firstname, and I want it to be capitalized by devise during registration.
I first ran:
rails generate migration add_username_to_users firstname:string
then
rake db:migrate
After that I added firstname to the configure_permitted_parameters in the application_controller.rb and updated the views. I basically used this but stripped out some unnecessary stuff.
I dont know where I should put the code for capitalizing the firstname and lastname (as well as some other validating). Any guidance would be appreciated. Thanks.
I think you should put capitalization of first and last names in your User model. Every time a user is saved, you can capitalize the first and last name. In addition, all validation (or attribute pre-processing/sanitization) can be done at the model level as well.
class User < ActiveRecord::Base
before_save :capitalize_names
def capitalize_names
self.firstname = firstname.camelcase
self.lastname = lastname.camelcase
end
end
before_create
Joe Kennedy's answer is correct - you should use the before_create ActiveRecord callback
The difference here is that Devise doesn't do anything with your actual data modelling - it basically just creates a series of controllers to handle the user registration & login processes
--
If you want to ensure certain attributes of your User model are saved in a particular style, you'll be best setting it in the model itself:
#app/models/user.rb
Class User < ActiveRecord::Base
before_create :set_firstname
private
def set_firstname
self.firstname.titeize
end
end
This should allow you to set the attribute to have the first letters of each word capitalized
--
System
An alternative would be to look at your system
Why are you insisting the data be stored this way? It seems very inefficient to save all your data in the same for the sake of styling.
I would use the CSS text-transform function to do this:
#app/assets/stylesheets/application.css
.first_name { text-transform: capitalize; }
#app/views/users/show.html.erb
<%= content_tag :span, #user.firstname, class: "first_name" %>
Best Solution Ever:
class Role < ApplicationRecord
before_save :capitalize_names
def capitalize_names
self.name.titlecase
end
end
Output will be:
'super admin'.titlecase
Super Admin
This should probably go in the User Controller (or whichever controller inherits from the Devise Controller and creates the new user). In the create method, before you save the user to the database, add whatever attributes you want to it (i.e. capitalizing the first letter) and then save it.
def create
User.create(email: params[:email], first_name: params[:first_name].capitalize)
end
Although I'd suggest you just output the capitalize in your views and not when saving.
Another question from rails newbie. I am using friendly_id gem with mysql in rails 3.x
This is a design problem (may be easy in rails). I am expecting advises from rails experts. I am building a library listing app. Where user can view library by "metro-area" or by "city" in it. For example:
I wish to have URLs like:
www.list.com/library/san-francisco-bay-area
or
www.list.com/library/san-francisco-bay-area/palo-alto/
In database I have tables:
library
-------
id, name, city_id, slug
name is slugged here and city_id is FK
city
----
city_id, name, metro_area_id, slug
name is slugged here and metro_area_id is FK
metro_area
----------
metro_area_id, name, state, slug
name is slugged here
So when a user points browser to www.list.com/library/san-francisco-bay-area/palo-alto
I wish to get list of libraries in san-francisco-bay-area/palo-alto. But my library table model is containing slug only for library's name. So how this URL can be parsed to find the city_id that can be used in library model and controller to get the list.
Please remember, I cannot rely only on the name of the city. I would have to find city_id of 'palo-alto' which is in metro 'san-francisco-bay-area'. Since slug for metro_area and city is in other tables, so what is the best way to design model and controller.
Show method of controller is:
def show
#user = Library.find(params[:id])
respond_to do |format|
format.html # show.html.erb
format.json { render json: #library }
end
end
and model is
class Library < ActiveRecord::Base
attr_accessible :name, :city_id, :slug
extend FriendlyId
friendly_id :name, use: :slugged
end
This will not work per my friendly URL requirement. So I would appreciate advice from experts :)
Maybe you have found your solution, I'm new to rails as well, so I'm just guessing this would work out.
Since you wanna display slugs from two different models. I'm assuming the way to display ids would be something like
www.list.com/libraries/:id/cities/:id/metro_areas/:id
Which can be done through editing the route file by adding Nested Resources
As for just displaying two ids like
www.list.com/library/:city_id/:metro_area_id
Rails guide refers it as Dynamic Segments
After that, it's just a matter of converting the ids to slugs.
I also found this in FriendlyId's documentation which is addressing to your case.
Hope it helps