Skip to content

Commit

Permalink
More clarity refactorings. Fixed README.rdoc not being included on ge…
Browse files Browse the repository at this point in the history
…m install.
  • Loading branch information
norman committed Nov 12, 2009
1 parent 2401b8e commit dee672f
Show file tree
Hide file tree
Showing 15 changed files with 92 additions and 116 deletions.
2 changes: 1 addition & 1 deletion MIT-LICENSE
@@ -1,4 +1,4 @@
Copyright (c) 2008 Norman Clarke and Adrian Mugnolo.
Copyright (c) 2008 Norman Clarke, Adrian Mugnolo and Emilio Tagua.

Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
Expand Down
13 changes: 11 additions & 2 deletions README.rdoc
Expand Up @@ -343,10 +343,19 @@ we love pull requests. :-)
Please report them on the {Github issue tracker}[http://github.com/norman/friendly_id/issues]
for this project.

If you have a bug to report, please include the following information:

* Stack trace and error message.
* Version information for FriendlyId, Rails and Ruby.
* Any snippets of relevant model, view or controller code that shows how your are using FriendlyId.

If you are able to, it helps even more if you can fork FriendlyId on Github, and
add a test that reproduces the error you are experiencing.

== Credits:

FriendlyId was created by {Norman Clarke}[mailto:norman@randomba.org],
{Adrian Mugnolo}[mailto:adrian@randomba.org], and {Emilio Tagua}[mailto:miloops@gmail.com].
FriendlyId was created by {Norman Clarke}[mailto:norman@njclarke.com],
{Adrian Mugnolo}[mailto:adrian@mugnolo.com], and {Emilio Tagua}[mailto:miloops@gmail.com].

We are grateful for many contributions from the Ruby and Rails community, in
particular from the following people:
Expand Down
1 change: 1 addition & 0 deletions Rakefile
Expand Up @@ -17,6 +17,7 @@ Hoe.spec "friendly_id" do
self.extra_dev_deps << ['newgem', ">= #{::Newgem::VERSION}"]
self.extra_dev_deps << ['sqlite3-ruby']
self.remote_rdoc_dir = ""
self.readme_file = "README.rdoc"
self.extra_rdoc_files = ["README.rdoc"]
end

Expand Down
4 changes: 1 addition & 3 deletions init.rb
@@ -1,3 +1 @@
# encoding: utf-8

require 'friendly_id'
require "friendly_id"
144 changes: 54 additions & 90 deletions lib/friendly_id.rb
@@ -1,109 +1,73 @@
# encoding: utf-8

require 'friendly_id/helpers'
require 'friendly_id/slug'
require "friendly_id/helpers"
require "friendly_id/slug"
require "friendly_id/sluggable_class_methods"
require "friendly_id/sluggable_instance_methods"
require "friendly_id/non_sluggable_class_methods"
require "friendly_id/non_sluggable_instance_methods"

# FriendlyId is a comprehensize Rails plugin/gem for slugging and permalinks.
module FriendlyId

# Default options for has_friendly_id.
DEFAULT_FRIENDLY_ID_OPTIONS = {
:max_length => 255,
:reserved => ["new", "index"],
:reserved_message => 'can not be "%s"',
:cache_column => nil,
:scope => nil,
:strip_diacritics => false,
:strip_non_ascii => false,
:use_slug => false
DEFAULT_OPTIONS = {
:max_length => 255,
:reserved => ["new", "index"],
:reserved_message => 'can not be "%s"'
}.freeze

VALID_KEYS = (DEFAULT_OPTIONS.keys + [
:cache_column,
:scope,
:strip_diacritics,
:stip_non_ascii,
:use_slug
]).freeze

# This error is raised when it's not possible to generate a unique slug.
class SlugGenerationError < StandardError ; end

module ClassMethods

# Set up an ActiveRecord model to use a friendly_id.
#
# The column argument can be one of your model's columns, or a method
# you use to generate the slug.
#
# Options:
# * <tt>:use_slug</tt> - Defaults to false. Use slugs when you want to use a non-unique text field for friendly ids.
# * <tt>:max_length</tt> - Defaults to 255. The maximum allowed length for a slug.
# * <tt>:cache_column</tt> - Defaults to nil. Use this column as a cache for generating to_param (experimental) Note that if you use this option, any calls to +attr_accessible+ must be made BEFORE any calls to has_friendly_id in your class.
# * <tt>:strip_diacritics</tt> - Defaults to false. If true, it will remove accents, umlauts, etc. from western characters.
# * <tt>:strip_non_ascii</tt> - Defaults to false. If true, it will all non-ascii ([^a-z0-9]) characters.
# * <tt>:reserved</tt> - Array of words that are reserved and can't be used as friendly_id's. For sluggable models, if such a word is used, it will raise a FriendlyId::SlugGenerationError. Defaults to ["new", "index"].
# * <tt>:reserved_message</tt> - The validation message that will be shown when a reserved word is used as a frindly_id. Defaults to '"%s" is reserved'.
#
# You can also optionally pass a block if you want to use your own custom
# slugnormalization routines rather than the default ones that come with
# friendly_id:
#
# require 'stringex'
# class Post < ActiveRecord::Base
# has_friendly_id :title, :use_slug => true do |text|
# # Use stringex to generate the friendly_id rather than the baked-in methods
# text.to_url
# end
# end
def has_friendly_id(method, options = {}, &block)
options.assert_valid_keys DEFAULT_FRIENDLY_ID_OPTIONS.keys
options = DEFAULT_FRIENDLY_ID_OPTIONS.merge(options).merge(:method => method)
write_inheritable_attribute :friendly_id_options, options
class_inheritable_accessor :friendly_id_options
class_inheritable_reader :slug_normalizer_block
friendly_id_options[:use_slug] ? set_up_with_slugs(&block) : set_up_without_slugs
end

private

def set_up_with_slugs(&block)
write_inheritable_attribute(:slug_normalizer_block, block) if block_given?
configure_cached_slugs
require 'friendly_id/sluggable_class_methods'
require 'friendly_id/sluggable_instance_methods'
# Set up an ActiveRecord model to use a friendly_id.
#
# The column argument can be one of your model's columns, or a method
# you use to generate the slug.
#
# Options:
# * <tt>:use_slug</tt> - Defaults to nil. Use slugs when you want to use a non-unique text field for friendly ids.
# * <tt>:max_length</tt> - Defaults to 255. The maximum allowed length for a slug.
# * <tt>:cache_column</tt> - Defaults to nil. Use this column as a cache for generating to_param (experimental) Note that if you use this option, any calls to +attr_accessible+ must be made BEFORE any calls to has_friendly_id in your class.
# * <tt>:strip_diacritics</tt> - Defaults to nil. If true, it will remove accents, umlauts, etc. from western characters.
# * <tt>:strip_non_ascii</tt> - Defaults to nil. If true, it will remove all non-ASCII characters.
# * <tt>:reserved</tt> - Array of words that are reserved and can't be used as friendly_id's. For sluggable models, if such a word is used, it will raise a FriendlyId::SlugGenerationError. Defaults to ["new", "index"].
# * <tt>:reserved_message</tt> - The validation message that will be shown when a reserved word is used as a frindly_id. Defaults to '"%s" is reserved'.
#
# You can also optionally pass a block if you want to use your own custom
# slug normalization routines rather than the default ones that come with
# friendly_id:
#
# require "stringex"
# class Post < ActiveRecord::Base
# has_friendly_id :title, :use_slug => true do |text|
# # Use stringex to generate the friendly_id rather than the baked-in methods
# text.to_url
# end
# end
def has_friendly_id(method, options = {}, &block)
options.assert_valid_keys VALID_KEYS
options = DEFAULT_OPTIONS.merge(options).merge(:method => method)
write_inheritable_attribute :friendly_id_options, options
class_inheritable_accessor :friendly_id_options
class_inheritable_reader :slug_normalizer_block
write_inheritable_attribute(:slug_normalizer_block, block) if block_given?
if friendly_id_options[:use_slug]
extend SluggableClassMethods
include SluggableInstanceMethods
has_many :slugs, :order => 'id DESC', :as => :sluggable, :dependent => :destroy
before_save :set_slug
after_save :set_slug_cache
end

def set_up_without_slugs
require 'friendly_id/non_sluggable_class_methods'
require 'friendly_id/non_sluggable_instance_methods'
else
extend NonSluggableClassMethods
include NonSluggableInstanceMethods
validate :validate_friendly_id
end

def configure_cached_slugs
unless friendly_id_options[:cache_column]
if columns.any? { |c| c.name == 'cached_slug' }
friendly_id_options[:cache_column] = :cached_slug
end
end
if friendly_id_options[:cache_column]
# only protect the column if the class is not already using attributes_accessible
attr_protected friendly_id_options[:cache_column].to_sym unless accessible_attributes
end
end
end

class << self

# Load FriendlyId if the gem is included in a Rails app.
def enable
return if ActiveRecord::Base.methods.include? 'has_friendly_id'
ActiveRecord::Base.class_eval { extend FriendlyId::ClassMethods }
end

end

end

if defined?(ActiveRecord)
FriendlyId::enable
class ActiveRecord::Base
extend FriendlyId
end
7 changes: 2 additions & 5 deletions lib/friendly_id/helpers.rb
@@ -1,7 +1,5 @@
# encoding: utf-8

module FriendlyId

module Helpers
# Calculate expected result size for find_some_with_friendly (taken from
# active_record/base.rb)
Expand All @@ -11,5 +9,4 @@ def expected_size(ids_and_names, options) #:nodoc:#
size
end
end

end
end
2 changes: 0 additions & 2 deletions lib/friendly_id/non_sluggable_class_methods.rb
@@ -1,5 +1,3 @@
# encoding: utf-8

module FriendlyId::NonSluggableClassMethods

include FriendlyId::Helpers
Expand Down
6 changes: 4 additions & 2 deletions lib/friendly_id/non_sluggable_instance_methods.rb
@@ -1,7 +1,9 @@
# encoding: utf-8

module FriendlyId::NonSluggableInstanceMethods

def self.included(base)
base.validate :validate_friendly_id
end

attr :found_using_friendly_id

# Was the record found using one of its friendly ids?
Expand Down
2 changes: 0 additions & 2 deletions lib/friendly_id/slug.rb
@@ -1,5 +1,3 @@
#encoding: utf-8

# A Slug is a unique, human-friendly identifier for an ActiveRecord.
class Slug < ActiveRecord::Base

Expand Down
2 changes: 0 additions & 2 deletions lib/friendly_id/sluggable_class_methods.rb
@@ -1,5 +1,3 @@
# encoding: utf-8

module FriendlyId::SluggableClassMethods

include FriendlyId::Helpers
Expand Down
17 changes: 15 additions & 2 deletions lib/friendly_id/sluggable_instance_methods.rb
@@ -1,7 +1,20 @@
# encoding: utf-8

module FriendlyId::SluggableInstanceMethods

def self.included(base)
base.has_many :slugs, :order => 'id DESC', :as => :sluggable, :dependent => :destroy
base.before_save :set_slug
base.after_save :set_slug_cache
unless base.friendly_id_options[:cache_column]
if base.columns.any? { |c| c.name == 'cached_slug' }
base.friendly_id_options[:cache_column] = :cached_slug
end
end
# only protect the column if the class is not already using attributes_accessible
if base.friendly_id_options[:cache_column] && !base.accessible_attributes
base.attr_protected base.friendly_id_options[:cache_column].to_sym
end
end

NUM_CHARS_RESERVED_FOR_FRIENDLY_ID_EXTENSION = 2

attr :finder_slug
Expand Down
2 changes: 0 additions & 2 deletions lib/friendly_id/version.rb
@@ -1,5 +1,3 @@
# encoding: utf-8

module FriendlyId #:nodoc:
module Version #:nodoc:
MAJOR = 2
Expand Down
2 changes: 1 addition & 1 deletion test/custom_slug_normalizer_test.rb
Expand Up @@ -7,7 +7,7 @@ class CustomSlugNormalizerTest < Test::Unit::TestCase
context "A slugged model using a custom slug generator" do

setup do
Thing.friendly_id_options = FriendlyId::DEFAULT_FRIENDLY_ID_OPTIONS.merge(:method => :name, :use_slug => true)
Thing.friendly_id_options = FriendlyId::DEFAULT_OPTIONS.merge(:method => :name, :use_slug => true)
end

teardown do
Expand Down
2 changes: 1 addition & 1 deletion test/slugged_model_test.rb
Expand Up @@ -7,7 +7,7 @@ class SluggedModelTest < Test::Unit::TestCase
context "A slugged model with default FriendlyId options" do

setup do
Post.friendly_id_options = FriendlyId::DEFAULT_FRIENDLY_ID_OPTIONS.merge(:method => :title, :use_slug => true)
Post.friendly_id_options = FriendlyId::DEFAULT_OPTIONS.merge(:method => :title, :use_slug => true)
@post = Post.new :title => "Test post", :content => "Test content", :published => true
@post.save!
end
Expand Down
2 changes: 1 addition & 1 deletion test/sti_test.rb
Expand Up @@ -7,7 +7,7 @@ class STIModelTest < Test::Unit::TestCase
context "A slugged model using single table inheritance" do

setup do
Novel.friendly_id_options = FriendlyId::DEFAULT_FRIENDLY_ID_OPTIONS.merge(:method => :title, :use_slug => true)
Novel.friendly_id_options = FriendlyId::DEFAULT_OPTIONS.merge(:method => :title, :use_slug => true)
@novel = Novel.new :title => "Test novel"
@novel.save!
end
Expand Down

0 comments on commit dee672f

Please sign in to comment.