Last active
April 1, 2020 19:48
-
-
Save duleorlovic/724b8ab1eb44d7f847ee to your computer and use it in GitHub Desktop.
Linkable concern instead of friendly_id gem
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
# app/models/concerns/linkable.rb | |
# | |
# Generate link that you can use to find resources | |
# Similar to friendly_id gem but much simpler (one file) and you can easily | |
# update instead of reading DSL for friendly_id | |
# You just need to define where to store link and which fields to use | |
# to generate link | |
# | |
# For example: | |
# rails g migration add_link_to_users link:string | |
# puts also: add_index :users, :link, unique: true | |
# rake db:migrate | |
# You can generate links for exists users | |
# User.all.map { |u| u.generate_link; u.save! } | |
# | |
# class User < ActiveRecord::Base | |
# include Linkable | |
# linkable :link, # where to store generated url | |
# [:business_name, :city], # try first with business name and city | |
# [:first_name, :last_name, :city], # if business_name is nil, use first_name, last_name | |
# [:business_name], # fall back if city is nil | |
# [:first_name, :last_name] # fallback if business_name is nil | |
# end | |
# | |
# You can find users by link | |
# User.find_by link: link | |
# you can change resources param name: | |
# resources :users, param: :link | |
# but it's better to create another route since link could change, but id won't | |
# resources :users do member do get 'by-link' end end | |
module Linkable | |
extend ActiveSupport::Concern | |
included do | |
def self.linkable(link_field, *candidate_field) | |
@@linkable_link_field = link_field | |
@@linkable_candidate_field = candidate_field | |
before_validation :generate_link, if: ->(obj) do | |
# TODO: just in case is changed, for example address_changed? | |
true | |
end | |
end | |
end | |
def generate_link | |
candidate_text = find_candidate_text | |
if candidate_text.blank? | |
errors.add(:base, "Can't generate link") | |
return | |
end | |
link = prepare_link candidate_text | |
link = check_duplication link | |
send "#{@@linkable_link_field}=", link | |
end | |
private | |
def find_candidate_text | |
candidate_text = nil | |
if @@linkable_candidate_field.class == Array | |
@@linkable_candidate_field.each do |candidate_field| | |
if candidate_field.class == Array | |
candidate_text_array = candidate_field.map { |f| send f } | |
next if candidate_text_array.select(&:blank?).present? | |
candidate_text = candidate_text_array.join('-') | |
else | |
candidate_text = send candidate_field | |
end | |
break if candidate_text.present? | |
end | |
else | |
candidate_text = send @@linkable_candidate_field | |
end | |
candidate_text | |
end | |
def check_duplication(link) | |
new_link = link | |
num = 1 | |
while self.class.where(@@linkable_link_field => new_link).where.not(id: id).exists? | |
num += 1 | |
new_link = "#{link}-#{num}" | |
end | |
new_link | |
end | |
def prepare_string(input) | |
# http://stackoverflow.com/questions/20224915/iconv-will-be-deprecated-in-the-future-transliterate | |
result_ascii = ActiveSupport::Multibyte::Unicode | |
.normalize(input, :kd) | |
.chars.grep(/\p{^Mn}/) | |
.join('') | |
result = result_ascii.strip.downcase.gsub(/\s+/, ".") | |
result | |
end | |
def prepare_link(input) | |
temp = prepare_string input | |
temp = temp.tr(".", "-") # dot . conflicts with rails routes request type | |
temp = temp.tr(",", "-") # comma , conflicts with joining customers website maps with , | |
temp = temp.gsub(/-+/, '-') # replace one or more --- with - | |
temp | |
end | |
end |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment