Browse files

Added generated guide from RDoc

  • Loading branch information...
1 parent c717fb9 commit 86b0c0900d09a50e162b8d4dfc6f50c8e64988af @norman committed Dec 19, 2011
View
2 .yardopts
@@ -1,3 +1,3 @@
---files=WhatsNew.md,Changelog.md
+--files=WhatsNew.md,Changelog.md,Guide.rdoc
--protected
--exclude lib/friendly_id/migration
View
466 Guide.rdoc
@@ -0,0 +1,466 @@
+#encoding: utf-8
+
+
+== About FriendlyId
+
+FriendlyId is an add-on to Ruby's Active Record that allows you to replace ids
+in your URLs with strings:
+
+ # without FriendlyId
+ http://example.com/states/4323454
+
+ # with FriendlyId
+ http://example.com/states/washington
+
+It requires few changes to your application code and offers flexibility,
+performance and a well-documented codebase.
+
+=== Core Concepts
+
+==== Slugs
+
+The concept of "slugs[http://en.wikipedia.org/wiki/Slug_(web_publishing)]" is at
+the heart of FriendlyId.
+
+A slug is the part of a URL which identifies a page using human-readable
+keywords, rather than an opaque identifier such as a numeric id. This can make
+your application more friendly both for users and search engine.
+
+==== Finders: Slugs Act Like Numeric IDs
+
+To the extent possible, FriendlyId lets you treat text-based identifiers like
+normal IDs. This means that you can perform finds with slugs just like you do
+with numeric ids:
+
+ Person.find(82542335)
+ Person.find("joe")
+
+
+== Setting Up FriendlyId in Your Model
+
+To use FriendlyId in your ActiveRecord models, you must first extend the
+FriendlyId module, then invoke the {FriendlyId::Base#friendly_id friendly_id}
+method to configure your desired options:
+
+ class Foo < ActiveRecord::Base
+ extend FriendlyId
+ friendly_id bar, :use => [:slugged, :i18n]
+ end
+
+The most important option is `:use`, which you use to tell FriendlyId which
+addons it should use. See the documentation for this method for a list of all
+available addons, or skim through the rest of the docs to get a high-level
+overview.
+
+=== The Default Setup: Simple Models
+
+The simplest way to use FriendlyId is with a model that has a uniquely indexed
+column with no spaces or special characters, and that is seldom or never
+updated. The most common example of this is a user name:
+
+ class User < ActiveRecord::Base
+ extend FriendlyId
+ friendly_id :login
+ validates_format_of :login, :with => /\A[a-z0-9]+\z/i
+ end
+
+ @user = User.find "joe" # the old User.find(1) still works, too
+ @user.to_param # returns "joe"
+ redirect_to @user # the URL will be /users/joe
+
+In this case, FriendlyId assumes you want to use the column as-is; it will never
+modify the value of the column, and your application should ensure that the
+value is unique and admissible in a URL:
+
+ class City < ActiveRecord::Base
+ extend FriendlyId
+ friendly_id :name
+ end
+
+ @city.find "Viña del Mar"
+ redirect_to @city # the URL will be /cities/Viña%20del%20Mar
+
+Writing the code to process an arbitrary string into a good identifier for use
+in a URL can be repetitive and surprisingly tricky, so for this reason it's
+often better and easier to use {FriendlyId::Slugged slugs}.
+
+
+== Slugged Models
+
+FriendlyId can use a separate column to store slugs for models which require
+some text processing.
+
+For example, blog applications typically use a post title to provide the basis
+of a search engine friendly URL. Such identifiers typically lack uppercase
+characters, use ASCII to approximate UTF-8 character, and strip out other
+characters which may make them aesthetically unappealing or error-prone when
+used in a URL.
+
+ class Post < ActiveRecord::Base
+ extend FriendlyId
+ friendly_id :title, :use => :slugged
+ end
+
+ @post = Post.create(:title => "This is the first post!")
+ @post.friendly_id # returns "this-is-the-first-post"
+ redirect_to @post # the URL will be /posts/this-is-the-first-post
+
+In general, use slugs by default unless you know for sure you don't need them.
+To activate the slugging functionality, use the {FriendlyId::Slugged} module.
+
+FriendlyId will generate slugs from a method or column that you specify, and
+store them in a field in your model. By default, this field must be named
++:slug+, though you may change this using the
+{FriendlyId::Slugged::Configuration#slug_column slug_column} configuration
+option. You should add an index to this column, and in most cases, make it
+unique. You may also wish to constrain it to NOT NULL, but this depends on your
+app's behavior and requirements.
+
+=== Example Setup
+
+ # your model
+ class Post < ActiveRecord::Base
+ extend FriendlyId
+ friendly_id :title, :use => :slugged
+ validates_presence_of :title, :slug, :body
+ end
+
+ # a migration
+ class CreatePosts < ActiveRecord::Migration
+ def self.up
+ create_table :posts do |t|
+ t.string :title, :null => false
+ t.string :slug, :null => false
+ t.text :body
+ end
+
+ add_index :posts, :slug, :unique => true
+ end
+
+ def self.down
+ drop_table :posts
+ end
+ end
+
+=== Working With Slugs
+
+==== Formatting
+
+By default, FriendlyId uses Active Support's
+paramaterize[http://api.rubyonrails.org/classes/ActiveSupport/Inflector.html#method-i-parameterize]
+method to create slugs. This method will intelligently replace spaces with
+dashes, and Unicode Latin characters with ASCII approximations:
+
+ movie = Movie.create! :title => "Der Preis fürs Überleben"
+ movie.slug #=> "der-preis-furs-uberleben"
+
+==== Uniqueness
+
+When you try to insert a record that would generate a duplicate friendly id,
+FriendlyId will append a sequence to the generated slug to ensure uniqueness:
+
+ car = Car.create :title => "Peugot 206"
+ car2 = Car.create :title => "Peugot 206"
+
+ car.friendly_id #=> "peugot-206"
+ car2.friendly_id #=> "peugot-206--2"
+
+==== Sequence Separator - The Two Dashes
+
+By default, FriendlyId uses two dashes to separate the slug from a sequence.
+
+You can change this with the {FriendlyId::Slugged::Configuration#sequence_separator
+sequence_separator} configuration option.
+
+==== Column or Method?
+
+FriendlyId always uses a method as the basis of the slug text - not a column. It
+first glance, this may sound confusing, but remember that Active Record provides
+methods for each column in a model's associated table, and that's what
+FriendlyId uses.
+
+Here's an example of a class that uses a custom method to generate the slug:
+
+ class Person < ActiveRecord::Base
+ friendly_id :name_and_location
+ def name_and_location
+ "#{name} from #{location}"
+ end
+ end
+
+ bob = Person.create! :name => "Bob Smith", :location => "New York City"
+ bob.friendly_id #=> "bob-smith-from-new-york-city"
+
+==== Providing Your Own Slug Processing Method
+
+You can override {FriendlyId::Slugged#normalize_friendly_id} in your model for
+total control over the slug format.
+
+==== Deciding When to Generate New Slugs
+
+Overriding {FriendlyId::Slugged#should_generate_new_friendly_id?} lets you
+control whether new friendly ids are created when a model is updated. For
+example, if you only want to generate slugs once and then treat them as
+read-only:
+
+ class Post < ActiveRecord::Base
+ extend FriendlyId
+ friendly_id :title, :use => :slugged
+
+ def should_generate_new_friendly_id?
+ new_record?
+ end
+ end
+
+ post = Post.create!(:title => "Hello world!")
+ post.slug #=> "hello-world"
+ post.title = "Hello there, world!"
+ post.save!
+ post.slug #=> "hello-world"
+
+==== Locale-specific Transliterations
+
+Active Support's +parameterize+ uses
+transliterate[http://api.rubyonrails.org/classes/ActiveSupport/Inflector.html#method-i-transliterate],
+which in turn can use I18n's transliteration rules to consider the current
+locale when replacing Latin characters:
+
+ # config/locales/de.yml
+ de:
+ i18n:
+ transliterate:
+ rule:
+ ü: "ue"
+ ö: "oe"
+ etc...
+
+ movie = Movie.create! :title => "Der Preis fürs Überleben"
+ movie.slug #=> "der-preis-fuers-ueberleben"
+
+This functionality was in fact taken from earlier versions of FriendlyId.
+
+==== Gotchas: Common Problems
+
+FriendlyId uses a before_validation callback to generate and set the slug. This
+means that if you create two model instances before saving them, it's possible
+they will generate the same slug, and the second save will fail.
+
+This can happen in two fairly normal cases: the first, when a model using nested
+attributes creates more than one record for a model that uses friendly_id. The
+second, in concurrent code, either in threads or multiple processes.
+
+===== Nested Attributes
+
+To solve the nested attributes issue, I recommend simply avoiding them when
+creating more than one nested record for a model that uses FriendlyId. See {this
+Github issue}[https://github.com/norman/friendly_id/issues/185] for discussion.
+
+===== Concurrency
+
+To solve the concurrency issue, I recommend locking the model's table against
+inserts while when saving the record. See {this Github
+issue}[https://github.com/norman/friendly_id/issues/180] for discussion.
+
+
+== History: Avoiding 404's When Slugs Change
+
+FriendlyId's {FriendlyId::History History} module adds the ability to store a
+log of a model's slugs, so that when its friendly id changes, it's still
+possible to perform finds by the old id.
+
+The primary use case for this is avoiding broken URLs.
+
+=== Setup
+
+In order to use this module, you must add a table to your database schema to
+store the slug records. FriendlyId provides a generator for this purpose:
+
+ rails generate friendly_id
+ rake db:migrate
+
+This will add a table named +friendly_id_slugs+, used by the {FriendlyId::Slug}
+model.
+
+=== Considerations
+
+This module is incompatible with the +:scoped+ module.
+
+Because recording slug history requires creating additional database records,
+this module has an impact on the performance of the associated model's +create+
+method.
+
+=== Example
+
+ class Post < ActiveRecord::Base
+ extend FriendlyId
+ friendly_id :title, :use => :history
+ end
+
+ class PostsController < ApplicationController
+
+ before_filter :find_post
+
+ ...
+
+ def find_post
+ Post.find params[:id]
+
+ # If an old id or a numeric id was used to find the record, then
+ # the request path will not match the post_path, and we should do
+ # a 301 redirect that uses the current friendly id.
+ if request.path != post_path(@post)
+ return redirect_to @post, :status => :moved_permanently
+ end
+ end
+ end
+
+
+== Unique Slugs by Scope
+
+The {FriendlyId::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.find("seattle").restaurants.find("joes-diner")
+ City.find("chicago").restaurants.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--2+.
+
+The value for the +:scope+ option can be the name of a +belongs_to+ relation, or
+a column.
+
+=== Finding Records by Friendly ID
+
+If you are using scopes your friendly ids may not be unique, so a simple find
+like
+
+ Restaurant.find("joes-diner")
+
+may return the wrong record. In these cases it's best to query through the
+relation:
+
+ @city.restaurants.find("joes-diner")
+
+Alternatively, you could pass the scope value as a query parameter:
+
+ Restaurant.find("joes-diner").where(:city_id => @city.id)
+
+
+=== Finding All Records That Match a Scoped ID
+
+Query the slug column directly:
+
+ Restaurant.find_all_by_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.find(params[:city_id])
+ @restaurant = @city.restaurants.find(params[:id])
+
+ # URLs:
+ http://example.org/cities/seattle/restaurants/joes-diner
+ http://example.org/cities/chicago/restaurants/joes-diner
+
+
+== Basic I18n
+
+This {FriendlyId::I18n i18n} adds very basic i18n support to FriendlyId.
+
+In order to use this module, your model must have a slug column for each locale.
+By default FriendlyId looks for columns named, for example, "slug_en",
+"slug_es", etc. The first part of the name can be configured by passing the
++:slug_column+ option if you choose. Note that as of 4.0.0.beta11, the column
+for the default locale must also include the locale in its name.
+
+=== Example migration
+
+ def self.up
+ create_table :posts do |t|
+ t.string :title
+ t.string :slug_en
+ t.string :slug_es
+ t.text :body
+ end
+ add_index :posts, :slug_en
+ add_index :posts, :slug_es
+ end
+
+=== Finds
+
+Finds will take into consideration the current locale:
+
+ I18n.locale = :es
+ Post.find("la-guerra-de-las-galaxas")
+ I18n.locale = :en
+ Post.find("star-wars")
+
+To find a slug by an explicit locale, perform the find inside a block
+passed to I18n's +with_locale+ method:
+
+ I18n.with_locale(:es) do
+ Post.find("la-guerra-de-las-galaxas")
+ end
+
+=== Creating Records
+
+When new records are created, the slug is generated for the current locale only.
+
+=== Translating Slugs
+
+To translate an existing record's friendly_id, use
+{FriendlyId::I18n::Model#set_friendly_id}. This will ensure that the slug you
+add is properly escaped, transliterated and sequenced:
+
+ post = Post.create :name => "Star Wars"
+ post.set_friendly_id("La guerra de las galaxas", :es)
+
+If you don't pass in a locale argument, FriendlyId::I18n will just use the
+current locale:
+
+ I18n.with_locale(:es) do
+ post.set_friendly_id("la-guerra-de-las-galaxas")
+ end
+
+
+== Reserved Words
+
+The {FriendlyId::Reserved Reserved} module adds the ability to exlude a list of
+words from use as FriendlyId slugs.
+
+By default, FriendlyId reserves the words "new" and "edit" when this module is
+included. You can configure this globally by using {FriendlyId.defaults
+FriendlyId.defaults}:
+
+ FriendlyId.defaults do |config|
+ config.use :reserved
+ # Reserve words for English and Spanish URLs
+ config.reserved_words = %w(new edit nueva nuevo editar)
+ end
View
3 README.md
@@ -34,6 +34,9 @@ new.
The current docs can always be found
[here](http://rubydoc.info/github/norman/friendly_id/master/frames).
+The best place to start is with the
+[Guide](http://rubydoc.info/github/norman/friendly_id/master/file/Guide.html),
+which compiles the top-level RDocs into one outlined document.
## Rails Quickstart
View
25 Rakefile
@@ -17,14 +17,37 @@ task :gem do
%x{gem build friendly_id.gemspec}
end
-task :yard do
+task :yard => :guide do
puts %x{bundle exec yard}
end
task :bench do
require File.expand_path("../bench", __FILE__)
end
+task :guide do
+ def read_comments(path)
+ path = File.expand_path("../#{path}", __FILE__)
+ match = File.read(path).match(/\n=begin(.*)\n=end/m)[1].to_s
+ match.split("\n").reject {|x| x =~ /^@/}.join("\n")
+ end
+
+ buffer = []
+
+ buffer << read_comments("lib/friendly_id.rb")
+ buffer << read_comments("lib/friendly_id/base.rb")
+ buffer << read_comments("lib/friendly_id/slugged.rb")
+ buffer << read_comments("lib/friendly_id/history.rb")
+ buffer << read_comments("lib/friendly_id/scoped.rb")
+ buffer << read_comments("lib/friendly_id/i18n.rb")
+ buffer << read_comments("lib/friendly_id/reserved.rb")
+
+ File.open("Guide.rdoc", "w") do |file|
+ file.write("#encoding: utf-8\n")
+ file.write(buffer.join("\n"))
+ end
+end
+
namespace :test do
desc "Run each test class in a separate process"
View
61 lib/friendly_id.rb
@@ -22,61 +22,26 @@
It requires few changes to your application code and offers flexibility,
performance and a well-documented codebase.
-=== Concepts
+=== Core Concepts
-Although FriendlyId helps with URLs, it does all of its work inside your models,
-not your routes.
+==== Slugs
-=== Simple Models
+The concept of "slugs[http://en.wikipedia.org/wiki/Slug_(web_publishing)]" is at
+the heart of FriendlyId.
-The simplest way to use FriendlyId is with a model that has a uniquely indexed
-column with no spaces or special characters, and that is seldom or never
-updated. The most common example of this is a user name:
+A slug is the part of a URL which identifies a page using human-readable
+keywords, rather than an opaque identifier such as a numeric id. This can make
+your application more friendly both for users and search engine.
- class User < ActiveRecord::Base
- extend FriendlyId
- friendly_id :login
- validates_format_of :login, :with => /\A[a-z0-9]+\z/i
- end
-
- @user = User.find "joe" # the old User.find(1) still works, too
- @user.to_param # returns "joe"
- redirect_to @user # the URL will be /users/joe
-
-In this case, FriendlyId assumes you want to use the column as-is; it will never
-modify the value of the column, and your application should ensure that the
-value is admissible in a URL:
-
- class City < ActiveRecord::Base
- extend FriendlyId
- friendly_id :name
- end
-
- @city.find "Viña del Mar"
- redirect_to @city # the URL will be /cities/Viña%20del%20Mar
-
-For this reason, it is often more convenient to use "slugs" rather than a single
-column.
-
-=== Slugged Models
-
-FriendlyId can uses a separate column to store slugs for models which require
-some processing of the friendly_id text. The most common example is a blog
-post's title, which may have spaces, uppercase characters, or other attributes
-you wish to modify to make them more suitable for use in URL's.
-
- class Post < ActiveRecord::Base
- extend FriendlyId
- friendly_id :title, :use => :slugged
- end
+==== Finders: Slugs Act Like Numeric IDs
- @post = Post.create(:title => "This is the first post!")
- @post.friendly_id # returns "this-is-the-first-post"
- redirect_to @post # the URL will be /posts/this-is-the-first-post
+To the extent possible, FriendlyId lets you treat text-based identifiers like
+normal IDs. This means that you can perform finds with slugs just like you do
+with numeric ids:
-In general, use slugs by default unless you know for sure you don't need them.
+ Person.find(82542335)
+ Person.find("joe")
-@author Norman Clarke
=end
module FriendlyId
View
77 lib/friendly_id/base.rb
@@ -1,5 +1,55 @@
module FriendlyId
- # Class methods that will be added to model classes that extend {FriendlyId}.
+=begin
+
+== Setting Up FriendlyId in Your Model
+
+To use FriendlyId in your ActiveRecord models, you must first extend the
+FriendlyId module, then invoke the {FriendlyId::Base#friendly_id friendly_id}
+method to configure your desired options:
+
+ class Foo < ActiveRecord::Base
+ extend FriendlyId
+ friendly_id bar, :use => [:slugged, :i18n]
+ end
+
+The most important option is `:use`, which you use to tell FriendlyId which
+addons it should use. See the documentation for this method for a list of all
+available addons, or skim through the rest of the docs to get a high-level
+overview.
+
+=== The Default Setup: Simple Models
+
+The simplest way to use FriendlyId is with a model that has a uniquely indexed
+column with no spaces or special characters, and that is seldom or never
+updated. The most common example of this is a user name:
+
+ class User < ActiveRecord::Base
+ extend FriendlyId
+ friendly_id :login
+ validates_format_of :login, :with => /\A[a-z0-9]+\z/i
+ end
+
+ @user = User.find "joe" # the old User.find(1) still works, too
+ @user.to_param # returns "joe"
+ redirect_to @user # the URL will be /users/joe
+
+In this case, FriendlyId assumes you want to use the column as-is; it will never
+modify the value of the column, and your application should ensure that the
+value is unique and admissible in a URL:
+
+ class City < ActiveRecord::Base
+ extend FriendlyId
+ friendly_id :name
+ end
+
+ @city.find "Viña del Mar"
+ redirect_to @city # the URL will be /cities/Viña%20del%20Mar
+
+Writing the code to process an arbitrary string into a good identifier for use
+in a URL can be repetitive and surprisingly tricky, so for this reason it's
+often better and easier to use {FriendlyId::Slugged slugs}.
+
+=end
module Base
# Configure FriendlyId's behavior in a model.
@@ -169,4 +219,29 @@ def relation_class
end
end
end
+
+ # Instance methods that will be added to all classes using FriendlyId.
+ module Model
+
+ attr_reader :current_friendly_id
+
+ # Convenience method for accessing the class method of the same name.
+ def friendly_id_config
+ self.class.friendly_id_config
+ end
+
+ # Get the instance's friendly_id.
+ def friendly_id
+ send friendly_id_config.query_field
+ end
+
+ # Either the friendly_id, or the numeric id cast to a string.
+ def to_param
+ if diff = changes[friendly_id_config.query_field]
+ diff.first
+ else
+ friendly_id.present? ? friendly_id : id.to_s
+ end
+ end
+ end
end
View
14 lib/friendly_id/history.rb
@@ -3,12 +3,16 @@
module FriendlyId
=begin
-This module adds the ability to store a log of a model's slugs, so that when its
-friendly id changes, it's still possible to perform finds by the old id.
+
+== History: Avoiding 404's When Slugs Change
+
+FriendlyId's {FriendlyId::History History} module adds the ability to store a
+log of a model's slugs, so that when its friendly id changes, it's still
+possible to perform finds by the old id.
The primary use case for this is avoiding broken URLs.
-== Setup
+=== Setup
In order to use this module, you must add a table to your database schema to
store the slug records. FriendlyId provides a generator for this purpose:
@@ -19,15 +23,15 @@ module FriendlyId
This will add a table named +friendly_id_slugs+, used by the {FriendlyId::Slug}
model.
-== Considerations
+=== Considerations
This module is incompatible with the +:scoped+ module.
Because recording slug history requires creating additional database records,
this module has an impact on the performance of the associated model's +create+
method.
-== Example
+=== Example
class Post < ActiveRecord::Base
extend FriendlyId
View
18 lib/friendly_id/i18n.rb
@@ -3,15 +3,18 @@
module FriendlyId
=begin
-This module adds very basic i18n support to FriendlyId.
+
+== Basic I18n
+
+This {FriendlyId::I18n i18n} adds very basic i18n support to FriendlyId.
In order to use this module, your model must have a slug column for each locale.
By default FriendlyId looks for columns named, for example, "slug_en",
"slug_es", etc. The first part of the name can be configured by passing the
+:slug_column+ option if you choose. Note that as of 4.0.0.beta11, the column
for the default locale must also include the locale in its name.
-== Example migration
+=== Example migration
def self.up
create_table :posts do |t|
@@ -24,7 +27,7 @@ def self.up
add_index :posts, :slug_es
end
-== Finds
+=== Finds
Finds will take into consideration the current locale:
@@ -40,14 +43,15 @@ def self.up
Post.find("la-guerra-de-las-galaxas")
end
-== Creating Records
+=== Creating Records
When new records are created, the slug is generated for the current locale only.
-== Translating Slugs
+=== Translating Slugs
-To translate an existing record's friendly_id, use {I18n::Model#set_friendly_id}. This will
-ensure that the slug you add is properly escaped, transliterated and sequenced:
+To translate an existing record's friendly_id, use
+{FriendlyId::I18n::Model#set_friendly_id}. This will ensure that the slug you
+add is properly escaped, transliterated and sequenced:
post = Post.create :name => "Star Wars"
post.set_friendly_id("La guerra de las galaxas", :es)
View
26 lib/friendly_id/model.rb
@@ -1,26 +0,0 @@
-module FriendlyId
- # Instance methods that will be added to all classes using FriendlyId.
- module Model
-
- attr_reader :current_friendly_id
-
- # Convenience method for accessing the class method of the same name.
- def friendly_id_config
- self.class.friendly_id_config
- end
-
- # Get the instance's friendly_id.
- def friendly_id
- send friendly_id_config.query_field
- end
-
- # Either the friendly_id, or the numeric id cast to a string.
- def to_param
- if diff = changes[friendly_id_config.query_field]
- diff.first
- else
- friendly_id.present? ? friendly_id : id.to_s
- end
- end
- end
-end
View
12 lib/friendly_id/reserved.rb
@@ -1,11 +1,15 @@
module FriendlyId
=begin
-This module adds the ability to exlude a list of words from use as
-FriendlyId slugs.
-By default, FriendlyId reserves the words "new" and "edit" when this module
-is included. You can configure this globally by using {FriendlyId.defaults FriendlyId.defaults}:
+== Reserved Words
+
+The {FriendlyId::Reserved Reserved} module adds the ability to exlude a list of
+words from use as FriendlyId slugs.
+
+By default, FriendlyId reserves the words "new" and "edit" when this module is
+included. You can configure this globally by using {FriendlyId.defaults
+FriendlyId.defaults}:
FriendlyId.defaults do |config|
config.use :reserved
View
8 lib/friendly_id/scoped.rb
@@ -3,7 +3,11 @@
module FriendlyId
=begin
-This module allows FriendlyId to generate unique slugs within a scope.
+
+== Unique Slugs by Scope
+
+The {FriendlyId::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+:
@@ -29,8 +33,6 @@ class City < ActiveRecord::Base
The value for the +:scope+ option can be the name of a +belongs_to+ relation, or
a column.
-== Tips For Working With Scoped Slugs
-
=== Finding Records by Friendly ID
If you are using scopes your friendly ids may not be unique, so a simple find
View
69 lib/friendly_id/slugged.rb
@@ -3,21 +3,37 @@
module FriendlyId
=begin
-This module adds in-table slugs to a model.
-Slugs are unique id strings that have been processed to remove or replace
-characters that a developer considers inconvenient for use in URLs. For example,
-blog applications typically use a post title to provide the basis of a search
-engine friendly URL:
+== Slugged Models
- "Gone With The Wind" -> "gone-with-the-wind"
+FriendlyId can use a separate column to store slugs for models which require
+some text processing.
-FriendlyId generates slugs from a method or column that you specify, and stores
-them in a field in your model. By default, this field must be named +:slug+,
-though you may change this using the
+For example, blog applications typically use a post title to provide the basis
+of a search engine friendly URL. Such identifiers typically lack uppercase
+characters, use ASCII to approximate UTF-8 character, and strip out other
+characters which may make them aesthetically unappealing or error-prone when
+used in a URL.
+
+ class Post < ActiveRecord::Base
+ extend FriendlyId
+ friendly_id :title, :use => :slugged
+ end
+
+ @post = Post.create(:title => "This is the first post!")
+ @post.friendly_id # returns "this-is-the-first-post"
+ redirect_to @post # the URL will be /posts/this-is-the-first-post
+
+In general, use slugs by default unless you know for sure you don't need them.
+To activate the slugging functionality, use the {FriendlyId::Slugged} module.
+
+FriendlyId will generate slugs from a method or column that you specify, and
+store them in a field in your model. By default, this field must be named
++:slug+, though you may change this using the
{FriendlyId::Slugged::Configuration#slug_column slug_column} configuration
-option. You should add an index to this field. You may also wish to constrain it
-to NOT NULL, but this depends on your app's behavior and requirements.
+option. You should add an index to this column, and in most cases, make it
+unique. You may also wish to constrain it to NOT NULL, but this depends on your
+app's behavior and requirements.
=== Example Setup
@@ -45,7 +61,9 @@ def self.down
end
end
-=== Slug Format
+=== Working With Slugs
+
+==== Formatting
By default, FriendlyId uses Active Support's
paramaterize[http://api.rubyonrails.org/classes/ActiveSupport/Inflector.html#method-i-parameterize]
@@ -55,7 +73,7 @@ def self.down
movie = Movie.create! :title => "Der Preis fürs Überleben"
movie.slug #=> "der-preis-furs-uberleben"
-==== Slug Uniqueness
+==== Uniqueness
When you try to insert a record that would generate a duplicate friendly id,
FriendlyId will append a sequence to the generated slug to ensure uniqueness:
@@ -66,9 +84,11 @@ def self.down
car.friendly_id #=> "peugot-206"
car2.friendly_id #=> "peugot-206--2"
-==== Changing the Slug Sequence Separator
+==== Sequence Separator - The Two Dashes
-You can do this with the {Slugged::Configuration#sequence_separator
+By default, FriendlyId uses two dashes to separate the slug from a sequence.
+
+You can change this with the {FriendlyId::Slugged::Configuration#sequence_separator
sequence_separator} configuration option.
==== Column or Method?
@@ -92,14 +112,15 @@ def name_and_location
==== Providing Your Own Slug Processing Method
-You can override {Slugged#normalize_friendly_id} in your model for total
-control over the slug format.
+You can override {FriendlyId::Slugged#normalize_friendly_id} in your model for
+total control over the slug format.
-==== Deciding when to generate new slugs
+==== Deciding When to Generate New Slugs
-Overriding {Slugged#should_generate_new_friendly_id?} lets you control whether
-new friendly ids are created when a model is updated. For example, if you only
-want to generate slugs once and then treat them as read-only:
+Overriding {FriendlyId::Slugged#should_generate_new_friendly_id?} lets you
+control whether new friendly ids are created when a model is updated. For
+example, if you only want to generate slugs once and then treat them as
+read-only:
class Post < ActiveRecord::Base
extend FriendlyId
@@ -137,7 +158,7 @@ def should_generate_new_friendly_id?
This functionality was in fact taken from earlier versions of FriendlyId.
-==== Common Problems
+==== Gotchas: Common Problems
FriendlyId uses a before_validation callback to generate and set the slug. This
means that if you create two model instances before saving them, it's possible
@@ -147,10 +168,14 @@ def should_generate_new_friendly_id?
attributes creates more than one record for a model that uses friendly_id. The
second, in concurrent code, either in threads or multiple processes.
+===== Nested Attributes
+
To solve the nested attributes issue, I recommend simply avoiding them when
creating more than one nested record for a model that uses FriendlyId. See {this
Github issue}[https://github.com/norman/friendly_id/issues/185] for discussion.
+===== Concurrency
+
To solve the concurrency issue, I recommend locking the model's table against
inserts while when saving the record. See {this Github
issue}[https://github.com/norman/friendly_id/issues/180] for discussion.

0 comments on commit 86b0c09

Please sign in to comment.