Skip to content

Commit

Permalink
Worked on documentation.
Browse files Browse the repository at this point in the history
  • Loading branch information
norman committed Aug 6, 2011
1 parent 1b2e8a7 commit ce7eb3c
Show file tree
Hide file tree
Showing 13 changed files with 169 additions and 150 deletions.
2 changes: 2 additions & 0 deletions .yardopts
@@ -0,0 +1,2 @@
--files=*.md
--protected
116 changes: 0 additions & 116 deletions Guide.md
Expand Up @@ -17,69 +17,6 @@ FriendlyId will always remain compatible with the current release of Rails, and
at least one stable release behind. That means that support for 3.0.x will not be
dropped until a stable release of 3.2 is out, or possibly longer.

### Using a Custom Method to Generate the Slug Text

FriendlyId can use either a column or a method to generate the slug text for
your model:

class City < ActiveRecord::Base
extend FriendlyId
belongs_to :country
friendly_id :name_and_country, :use => :slugged

def name_and_country
#{name} #{country.name}
end

end

@country = Country.create(:name => "Argentina")
@city = City.create(:name => "Buenos Aires", :country => @country)
@city.friendly_id # will be "buenos-aires-argentina"

One word of caution: in the example above, if the country's name were updated,
say, to "Argentine Republic", the city's friendly_id would not be
automatically updated. For this reason, it's a good idea to avoid using
frequently-updated relations as a part of the friendly_id.

## Using a Custom Method to Process the Slug Text

If the built-in slug text handling options don't work for your application,
you can override the `normalize_friendly_id` method in your model class in
order to fine-tune the output:

class City < ActiveRecord::Base
extend FriendlyId
friendly_id :whatever, :use => :slugged

def normalize_friendly_id(text)
my_text_modifier_method(text)
end

end

The `normalize_friendly_id` method takes a single argument and receives an
instance of {FriendlyId::SlugString}, a class which wraps a regular Ruby string
with additional formatting options.

### Converting non-Latin characters to ASCII with Babosa

Babosa offers the ability to idiomatically transliterate non-ASCII characters
to ASCII:

"Jürgen".to_slug.normalize! #=> "Jurgen"
"Jürgen".to_slug.normalize! :transliterate => :german #=> "Juergen"

Using Babosa with FriendlyId is a simple matter of installing and requiring
the `babosa` gem, and overriding the `normalize_friendly_id` method in your
model:

class City < ActiveRecord::Base
def normalize_friendly_id(text)
text.slug.normalize!
end
end

## Redirecting to the Current Friendly URL

FriendlyId can maintain a history of your record's older slugs, so if your
Expand Down Expand Up @@ -111,45 +48,6 @@ record's friendly_id changes, your URL's won't break.
end
end

## Non-unique Slugs

FriendlyId will append a arbitrary number to the end of the id to keep it
unique if necessary:

/posts/new-version-released
/posts/new-version-released--2
/posts/new-version-released--3
...
etc.

Note that the number is preceded by "--" rather than "-" to distinguish it from
the rest of the slug. This is important to enable having slugs like:

/cars/peugeot-206
/cars/peugeot-206--2

You can configure the separator string used by your model by setting the
`:sequence_separator` option in `friendly_id`:

friendly_id :title, :use => :slugged, :sequence_separator => ":"

You can also override the default used in
{FriendlyId::Configuration::DEFAULTS} to set the value for any model using
FriendlyId. If you change this value in an existing application, be sure to
{file:Guide.md#regenerating_slugs regenerate the slugs} afterwards.

For reasons I hope are obvious, you can't change this value to "-". If you try,
FriendlyId will raise an error.

## Reserved Words

When you use slugs, FriendlyId adds a validation to avoid using "new" and
"index" as slugs. You can control the default reserved words by changing the
value in `FriendlyId::Configuration::DEFAULTS[:reserved_words]`.

## Scoped Slugs


# Misc tips

## Default Scopes
Expand Down Expand Up @@ -177,17 +75,3 @@ column.
These benchmarks can give you an idea of FriendlyId's impact on the
performance of your application. Of course your results may vary.

activerecord (3.0.9)
ruby 1.9.2p180 (2011-02-18 revision 30909) [x86_64-darwin10.6.0]
friendly_id (4.0.0.beta1)
sqlite3 (1.3.3) gem
sqlite3 3.6.12 in-memory database


user system total real
find (without FriendlyId) 0.300000 0.000000 0.300000 ( 0.306729)
find (in-table slug) 0.350000 0.000000 0.350000 ( 0.351760)
find (external slug) 3.320000 0.000000 3.320000 ( 3.326749)
insert (without FriendlyId) 0.810000 0.010000 0.820000 ( 0.810513)
insert (in-table-slug) 1.740000 0.000000 1.740000 ( 1.743511)
insert (external slug) 3.540000 0.010000 3.550000 ( 3.544898)
6 changes: 6 additions & 0 deletions README.md
Expand Up @@ -70,6 +70,12 @@ FriendlyId is compatible with Active Record **3.0** and **3.1**.

GET http://localhost:3000/users/joe-schmoe

## Benchmarks

The latest benchmarks for FriendlyId are maintained
[here](https://gist.github.com/1129745).


## Bugs

Please report them on the [Github issue
Expand Down
2 changes: 1 addition & 1 deletion Rakefile
Expand Up @@ -45,7 +45,7 @@ task :gem do
end

task :yard do
puts %x{bundle exec yard doc --files=*.md}
puts %x{bundle exec yard}
end

task :bench do
Expand Down
2 changes: 1 addition & 1 deletion lib/friendly_id.rb
Expand Up @@ -94,7 +94,7 @@ module FriendlyId
# Previous versions of FriendlyId simply patched ActiveRecord::Base, but this
# version tries to be less invasive.
#
# In addition to adding {FriendlyId::Base.friendly_id friendly_id}, the class
# In addition to adding {FriendlyId::Base#friendly_id friendly_id}, the class
# instance variable +@friendly_id_config+ is added. This variable is an
# instance of an anonymous subclass of {FriendlyId::Configuration}. This
# allows subsequently loaded modules like {FriendlyId::Slugged} and
Expand Down
18 changes: 15 additions & 3 deletions lib/friendly_id/base.rb
@@ -1,5 +1,5 @@
module FriendlyId
# Class methods that will be added to ActiveRecord::Base.
# Class methods that will be added to model classes that extend {FriendlyId}.
module Base

# Configure FriendlyId's behavior in a model.
Expand Down Expand Up @@ -54,24 +54,35 @@ module Base
#
# @option options [Symbol] :use The name of an addon to use. By default,
# FriendlyId provides {FriendlyId::Slugged :slugged},
# {FriendlyId::History :history}, {FriendlyId::Reserved}, and
# {FriendlyId::History :history}, {FriendlyId::Reserved :reserved}, and
# {FriendlyId::Scoped :scoped}.
# @options options [Array] :reserved_words Available when using +:reserved+,
#
# @option options [Array] :reserved_words Available when using +:reserved+,
# which is loaded by default. Sets an array of words banned for use as
# the basis of a friendly_id. By default this includes "edit" and "new".
#
# @option options [Symbol] :scope Available when using +:scoped+.
# Sets the relation or column used to scope generated friendly ids. This
# option has no default value.
#
# @option options [Symbol] :sequence_separator Available when using +:slugged+.
# Configures the sequence of characters used to separate a slug from a
# sequence. Defaults to +--+.
#
# @option options [Symbol] :slug_column Available when using +:slugged+.
# Configures the name of the column where FriendlyId will store the slug.
# Defaults to +:slug+.
#
# @option options [Symbol] :slug_sequencer_class Available when using +:slugged+.
# Sets the class used to generate unique slugs. You should not specify this
# unless you're doing some extensive hacking on FriendlyId. Defaults to
# {FriendlyId::SlugSequencer}.
#
# @yield Provides access to the model class's friendly_id_config, which
# allows an alternate configuration syntax, and conditional configuration
# logic.
#
# @yieldparam config The model class's {FriendlyId::Configuration friendly_id_config}.
def friendly_id(base = nil, options = {}, &block)
yield @friendly_id_config if block_given?
@friendly_id_config.use options.delete :use
Expand All @@ -82,6 +93,7 @@ def friendly_id(base = nil, options = {}, &block)
include Model
end

# Returns the model class's {FriendlyId::Configuration friendly_id_config}.
def friendly_id_config
@friendly_id_config
end
Expand Down
1 change: 1 addition & 0 deletions lib/friendly_id/configuration.rb
Expand Up @@ -27,6 +27,7 @@ class Configuration
# end
attr_accessor :base

# The default configuration options.
attr_reader :defaults

# The model class that this configuration belongs to.
Expand Down
8 changes: 8 additions & 0 deletions lib/friendly_id/finder_methods.rb
Expand Up @@ -4,6 +4,14 @@ module FinderMethods

protected

# FriendlyId overrides this method to make it possible to use friendly id's
# identically to numeric ids in finders.
#
# @example
# person = Person.find(123)
# person = Person.find("joe")
#
# @see FriendlyId::ObjectUtils
def find_one(id)
return super if !@klass.respond_to?(:friendly_id) || id.unfriendly_id?
where(@klass.friendly_id_config.query_field => id).first or super
Expand Down
22 changes: 16 additions & 6 deletions lib/friendly_id/object_utils.rb
@@ -1,14 +1,26 @@
module FriendlyId
# Utility methods that are in Object because it's impossible to predict what
# kinds of objects get passed into FinderMethods#find_one and
# Model#normalize_friendly_id.
# Utility methods for determining whether any object is a friendly id.
#
# Monkey-patching Object is a somewhat extreme measure not to be taken lightly
# by libraries, but in this case I decided to do it because to me, it feels
# cleaner than adding a module method to {FriendlyId}. I've given the methods
# names that unambigously refer to the library of their origin, which should
# be sufficient to avoid conflicts with other libraries.
module ObjectUtils

# True is the id is definitely friendly, false if definitely unfriendly,
# else nil.
#
# An object is considired "definitely unfriendly" if its class is or
# inherits from Numeric, Symbol or ActiveRecord::Base.
#
# An object is considered "definitely friendly" if it responds to +to_i+,
# and its value when cast to an integer and then back to a string is
# different from its value when merely cast to a string:
#
# 123.friendly_id? #=> false
# "123".friendly_id? #=> nil
# "abc123".friendly_id? #=> true
def friendly_id?
if [Numeric, Symbol, ActiveRecord::Base].detect {|klass| self.class < klass}
false
Expand All @@ -25,6 +37,4 @@ def unfriendly_id?
end
end

class Object
include FriendlyId::ObjectUtils
end
Object.send :include, FriendlyId::ObjectUtils
23 changes: 21 additions & 2 deletions lib/friendly_id/reserved.rb
@@ -1,24 +1,43 @@
module FriendlyId

# This module adds the ability to exlude a list of words from use as
# FriendlyId slugs.
=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}:
FriendlyId.defaults do |config|
config.use :reserved
# Reserve words for English and Spanish URLs
config.reserved_words = %w(new edit nueva nuevo editar)
end
=end
module Reserved

# When included, this module adds configuration options to the model class's
# friendly_id_config.
def self.included(model_class)
model_class.class_eval do
friendly_id_config.class.send :include, Reserved::Configuration
friendly_id_config.defaults[:reserved_words] ||= ["new", "edit"]
end
end

# This module adds the +:reserved_words+ configuration option to
# {FriendlyId::Configuration FriendlyId::Configuration}.
module Configuration
attr_writer :reserved_words

# Overrides {FriendlyId::Configuration#base} to add a validation to the
# model class.
def base=(base)
super
reserved_words = model_class.friendly_id_config.reserved_words
model_class.validates_exclusion_of base, :in => reserved_words
end

# An array of words forbidden as slugs.
def reserved_words
@reserved_words ||= @defaults[:reserved_words]
end
Expand Down
9 changes: 5 additions & 4 deletions lib/friendly_id/scoped.rb
Expand Up @@ -101,10 +101,11 @@ module Configuration

# Gets the scope column.
#
# Checks to see if the +:scope+ option passed to {#friendly_id}
# refers to a relation, and if so, returns the realtion's foreign key.
# Otherwise it assumes the option value was the name of column and returns
# it cast to a String.
# Checks to see if the +:scope+ option passed to
# {FriendlyId::Base#friendly_id} refers to a relation, and if so, returns
# the realtion's foreign key. Otherwise it assumes the option value was
# the name of column and returns it cast to a String.
#
# @return String The scope column
def scope_column
(model_class.reflections[@scope].try(:association_foreign_key) || @scope).to_s
Expand Down
3 changes: 3 additions & 0 deletions lib/friendly_id/slug.rb
@@ -1,3 +1,6 @@
# A FriendlyId slug stored in an external table.
#
# @see FriendlyId::History
class FriendlyIdSlug < ActiveRecord::Base
belongs_to :sluggable, :polymorphic => true
end

0 comments on commit ce7eb3c

Please sign in to comment.