Module: Slugifiable::Model
- Extended by:
- ActiveSupport::Concern
- Defined in:
- lib/slugifiable/model.rb
Constant Summary collapse
- DEFAULT_SLUG_GENERATION_STRATEGY =
This concern makes objects have a string slug based on their ID or another specified attribute
To use, include this in any ActiveRecord model: “‘ include Slugifiable::Model “`
By default all slugs will be a string computed from the record ID: “‘ generate_slug_based_on :id “`
but optionally, you can also specify to compute the slug as a number: “‘ generate_slug_based_on id: :number “`
or compute the slug based off any other attribute: “‘ generate_slug_based_on :name “`
:compute_slug_as_string- DEFAULT_SLUG_STRING_LENGTH =
11- DEFAULT_SLUG_NUMBER_LENGTH =
6- MAX_SLUG_GENERATION_ATTEMPTS =
Maximum number of attempts to generate a unique slug before falling back to timestamp-based suffix
10
Instance Method Summary collapse
- #compute_slug ⇒ Object
- #compute_slug_as_number(length = DEFAULT_SLUG_NUMBER_LENGTH) ⇒ Object
- #compute_slug_as_string(length = DEFAULT_SLUG_STRING_LENGTH) ⇒ Object
- #compute_slug_based_on_attribute(attribute_name) ⇒ Object
- #method_missing(missing_method, *args, &block) ⇒ Object
Dynamic Method Handling
This class handles dynamic methods through the method_missing method
#method_missing(missing_method, *args, &block) ⇒ Object
53 54 55 56 57 58 59 |
# File 'lib/slugifiable/model.rb', line 53 def method_missing(missing_method, *args, &block) if missing_method.to_s == "slug" && !self.methods.include?(:slug) compute_slug else super end end |
Instance Method Details
#compute_slug ⇒ Object
61 62 63 64 65 66 67 68 69 70 71 |
# File 'lib/slugifiable/model.rb', line 61 def compute_slug strategy, = determine_slug_generation_method length = [:length] if .is_a?(Hash) if strategy == :compute_slug_based_on_attribute self.send(strategy, ) else self.send(strategy, length) end end |
#compute_slug_as_number(length = DEFAULT_SLUG_NUMBER_LENGTH) ⇒ Object
78 79 80 81 |
# File 'lib/slugifiable/model.rb', line 78 def compute_slug_as_number(length = DEFAULT_SLUG_NUMBER_LENGTH) length ||= DEFAULT_SLUG_NUMBER_LENGTH generate_random_number_based_on_id_hex(length) end |
#compute_slug_as_string(length = DEFAULT_SLUG_STRING_LENGTH) ⇒ Object
73 74 75 76 |
# File 'lib/slugifiable/model.rb', line 73 def compute_slug_as_string(length = DEFAULT_SLUG_STRING_LENGTH) length ||= DEFAULT_SLUG_STRING_LENGTH (Digest::SHA2.hexdigest self.id.to_s).first(length) end |
#compute_slug_based_on_attribute(attribute_name) ⇒ Object
83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 |
# File 'lib/slugifiable/model.rb', line 83 def compute_slug_based_on_attribute(attribute_name) # This method generates a slug from either: # 1. A database column (e.g. generate_slug_based_on :title) # 2. An instance method (e.g. generate_slug_based_on :title_with_location) # # Priority: # - Database columns take precedence over methods with the same name # - Falls back to methods if no matching column exists # - Falls back to ID-based slug if neither exists # # Flow: # 1. Check if source exists (DB column first, then method) # 2. Get raw value # 3. Parameterize (convert "My Title" -> "my-title") # 4. Ensure uniqueness # 5. Fallback to random number if anything fails # First check if we can get a value from the database has_attribute = self.attributes.include?(attribute_name.to_s) # Only check for methods if no DB attribute exists # We check all method types to be thorough responds_to_method = !has_attribute && ( self.class.method_defined?(attribute_name) || self.class.private_method_defined?(attribute_name) || self.class.protected_method_defined?(attribute_name) ) # If we can't get a value from either source, fallback to using the record's ID return compute_slug_as_string unless has_attribute || responds_to_method # Get and clean the raw value (e.g. " My Title " -> "My Title") # Works for both DB attributes and methods thanks to Ruby's send raw_value = self.send(attribute_name) return generate_random_number_based_on_id_hex if raw_value.nil? # Convert to URL-friendly format # e.g. "My Title" -> "my-title" base_slug = raw_value.to_s.strip.parameterize return generate_random_number_based_on_id_hex if base_slug.blank? # Handle duplicate slugs by adding a random suffix if needed # e.g. "my-title" -> "my-title-123456" unique_slug = generate_unique_slug(base_slug) unique_slug.presence || generate_random_number_based_on_id_hex end |