Module: FriendlyId::Scoped
- Defined in:
- lib/friendly_id/scoped.rb
Overview
Unique Slugs by Scope
The Scoped module allows FriendlyId to generate unique slugs within a scope.
This allows, for example, two restaurants in different cities to have the slug
joes-diner
:
class Restaurant < ActiveRecord::Base
extend FriendlyId
belongs_to :city
friendly_id :name, :use => :scoped, :scope => :city
end
class City < ActiveRecord::Base
extend FriendlyId
has_many :restaurants
friendly_id :name, :use => :slugged
end
City.friendly.find("seattle").restaurants.friendly.find("joes-diner")
City.friendly.find("chicago").restaurants.friendly.find("joes-diner")
Without :scoped in this case, one of the restaurants would have the slug
joes-diner
and the other would have joes-diner-f9f3789a-daec-4156-af1d-fab81aa16ee5
.
The value for the :scope
option can be the name of a belongs_to
relation, or
a column.
Additionally, the :scope
option can receive an array of scope values:
class Cuisine < ActiveRecord::Base
extend FriendlyId
has_many :restaurants
friendly_id :name, :use => :slugged
end
class City < ActiveRecord::Base
extend FriendlyId
has_many :restaurants
friendly_id :name, :use => :slugged
end
class Restaurant < ActiveRecord::Base
extend FriendlyId
belongs_to :city
friendly_id :name, :use => :scoped, :scope => [:city, :cuisine]
end
All supplied values will be used to determine scope.
Finding Records by Friendly ID
If you are using scopes your friendly ids may not be unique, so a simple find like:
Restaurant.friendly.find("joes-diner")
may return the wrong record. In these cases it's best to query through the relation:
@city.restaurants.friendly.find("joes-diner")
Alternatively, you could pass the scope value as a query parameter:
Restaurant.where(:city_id => @city.id).friendly.find("joes-diner")
Finding All Records That Match a Scoped ID
Query the slug column directly:
Restaurant.where(:slug => "joes-diner")
Routes for Scoped Models
Recall that FriendlyId is a database-centric library, and does not set up any routes for scoped models. You must do this yourself in your application. Here's an example of one way to set this up:
# in routes.rb
resources :cities do
resources :restaurants
end
# in views
<%= link_to 'Show', [@city, @restaurant] %>
# in controllers
@city = City.friendly.find(params[:city_id])
@restaurant = @city.restaurants.friendly.find(params[:id])
# URLs:
http://example.org/cities/seattle/restaurants/joes-diner
http://example.org/cities/chicago/restaurants/joes-diner
Defined Under Namespace
Modules: Configuration
Class Method Summary collapse
-
.included(model_class) ⇒ Object
Sets up behavior and configuration options for FriendlyId's scoped slugs feature.
-
.setup(model_class) ⇒ Object
FriendlyId::Config.use will invoke this method when present, to allow loading dependent modules prior to overriding them when necessary.
Instance Method Summary collapse
- #scope_for_slug_generator ⇒ Object private
- #serialized_scope ⇒ Object
- #should_generate_new_friendly_id? ⇒ Boolean
- #slug_generator ⇒ Object private
Class Method Details
.included(model_class) ⇒ Object
Sets up behavior and configuration options for FriendlyId's scoped slugs feature.
112 113 114 115 116 |
# File 'lib/friendly_id/scoped.rb', line 112 def self.included(model_class) model_class.class_eval do friendly_id_config.class.send :include, Configuration end end |
.setup(model_class) ⇒ Object
FriendlyId::Config.use will invoke this method when present, to allow loading dependent modules prior to overriding them when necessary.
106 107 108 |
# File 'lib/friendly_id/scoped.rb', line 106 def self.setup(model_class) model_class.friendly_id_config.use :slugged end |
Instance Method Details
#scope_for_slug_generator ⇒ Object (private)
122 123 124 125 126 127 128 129 130 131 132 |
# File 'lib/friendly_id/scoped.rb', line 122 def scope_for_slug_generator if friendly_id_config.uses?(:History) return super end relation = self.class.base_class.unscoped.friendly friendly_id_config.scope_columns.each do |column| relation = relation.where(column => send(column)) end primary_key_name = self.class.primary_key relation.where(self.class.arel_table[primary_key_name].not_eq(send(primary_key_name))) end |
#serialized_scope ⇒ Object
118 119 120 |
# File 'lib/friendly_id/scoped.rb', line 118 def serialized_scope friendly_id_config.scope_columns.sort.map { |column| "#{column}:#{send(column)}" }.join(",") end |
#should_generate_new_friendly_id? ⇒ Boolean
140 141 142 |
# File 'lib/friendly_id/scoped.rb', line 140 def should_generate_new_friendly_id? (changed & friendly_id_config.scope_columns).any? || super end |
#slug_generator ⇒ Object (private)
135 136 137 |
# File 'lib/friendly_id/scoped.rb', line 135 def slug_generator friendly_id_config.slug_generator_class.new(scope_for_slug_generator, friendly_id_config) end |