From dee672f0871c15ec7379d148302a1abe826f3b12 Mon Sep 17 00:00:00 2001 From: Norman Clarke Date: Thu, 12 Nov 2009 11:38:43 -0300 Subject: [PATCH] More clarity refactorings. Fixed README.rdoc not being included on gem install. --- MIT-LICENSE | 2 +- README.rdoc | 13 +- Rakefile | 1 + init.rb | 4 +- lib/friendly_id.rb | 144 +++++++----------- lib/friendly_id/helpers.rb | 7 +- .../non_sluggable_class_methods.rb | 2 - .../non_sluggable_instance_methods.rb | 6 +- lib/friendly_id/slug.rb | 2 - lib/friendly_id/sluggable_class_methods.rb | 2 - lib/friendly_id/sluggable_instance_methods.rb | 17 ++- lib/friendly_id/version.rb | 2 - test/custom_slug_normalizer_test.rb | 2 +- test/slugged_model_test.rb | 2 +- test/sti_test.rb | 2 +- 15 files changed, 92 insertions(+), 116 deletions(-) diff --git a/MIT-LICENSE b/MIT-LICENSE index 50505c10e..5661794c8 100644 --- a/MIT-LICENSE +++ b/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 diff --git a/README.rdoc b/README.rdoc index ea606e5e7..5511dfa31 100644 --- a/README.rdoc +++ b/README.rdoc @@ -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: diff --git a/Rakefile b/Rakefile index 371939ecc..75e01d886 100644 --- a/Rakefile +++ b/Rakefile @@ -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 diff --git a/init.rb b/init.rb index 9b168e3f4..34335bc49 100644 --- a/init.rb +++ b/init.rb @@ -1,3 +1 @@ -# encoding: utf-8 - -require 'friendly_id' \ No newline at end of file +require "friendly_id" diff --git a/lib/friendly_id.rb b/lib/friendly_id.rb index 5fa7038e4..4fcd33a5e 100644 --- a/lib/friendly_id.rb +++ b/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: - # * :use_slug - Defaults to false. Use slugs when you want to use a non-unique text field for friendly ids. - # * :max_length - Defaults to 255. The maximum allowed length for a slug. - # * :cache_column - 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. - # * :strip_diacritics - Defaults to false. If true, it will remove accents, umlauts, etc. from western characters. - # * :strip_non_ascii - Defaults to false. If true, it will all non-ascii ([^a-z0-9]) characters. - # * :reserved - 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"]. - # * :reserved_message - 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: + # * :use_slug - Defaults to nil. Use slugs when you want to use a non-unique text field for friendly ids. + # * :max_length - Defaults to 255. The maximum allowed length for a slug. + # * :cache_column - 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. + # * :strip_diacritics - Defaults to nil. If true, it will remove accents, umlauts, etc. from western characters. + # * :strip_non_ascii - Defaults to nil. If true, it will remove all non-ASCII characters. + # * :reserved - 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"]. + # * :reserved_message - 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 diff --git a/lib/friendly_id/helpers.rb b/lib/friendly_id/helpers.rb index 4a248cbe3..4f8050ebe 100644 --- a/lib/friendly_id/helpers.rb +++ b/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) @@ -11,5 +9,4 @@ def expected_size(ids_and_names, options) #:nodoc:# size end end - -end \ No newline at end of file +end diff --git a/lib/friendly_id/non_sluggable_class_methods.rb b/lib/friendly_id/non_sluggable_class_methods.rb index 96218ecfc..c688a85c1 100644 --- a/lib/friendly_id/non_sluggable_class_methods.rb +++ b/lib/friendly_id/non_sluggable_class_methods.rb @@ -1,5 +1,3 @@ -# encoding: utf-8 - module FriendlyId::NonSluggableClassMethods include FriendlyId::Helpers diff --git a/lib/friendly_id/non_sluggable_instance_methods.rb b/lib/friendly_id/non_sluggable_instance_methods.rb index b5b0f155a..aedb53f3c 100644 --- a/lib/friendly_id/non_sluggable_instance_methods.rb +++ b/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? diff --git a/lib/friendly_id/slug.rb b/lib/friendly_id/slug.rb index a19504299..5a4502bc2 100644 --- a/lib/friendly_id/slug.rb +++ b/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 diff --git a/lib/friendly_id/sluggable_class_methods.rb b/lib/friendly_id/sluggable_class_methods.rb index ea9bb7cb7..0436338b2 100644 --- a/lib/friendly_id/sluggable_class_methods.rb +++ b/lib/friendly_id/sluggable_class_methods.rb @@ -1,5 +1,3 @@ -# encoding: utf-8 - module FriendlyId::SluggableClassMethods include FriendlyId::Helpers diff --git a/lib/friendly_id/sluggable_instance_methods.rb b/lib/friendly_id/sluggable_instance_methods.rb index 5033dd059..0c62f4a91 100644 --- a/lib/friendly_id/sluggable_instance_methods.rb +++ b/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 diff --git a/lib/friendly_id/version.rb b/lib/friendly_id/version.rb index 41141be2a..674b07752 100644 --- a/lib/friendly_id/version.rb +++ b/lib/friendly_id/version.rb @@ -1,5 +1,3 @@ -# encoding: utf-8 - module FriendlyId #:nodoc: module Version #:nodoc: MAJOR = 2 diff --git a/test/custom_slug_normalizer_test.rb b/test/custom_slug_normalizer_test.rb index 145668c3e..591c35f89 100644 --- a/test/custom_slug_normalizer_test.rb +++ b/test/custom_slug_normalizer_test.rb @@ -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 diff --git a/test/slugged_model_test.rb b/test/slugged_model_test.rb index 1a31b0474..bfd185cdb 100644 --- a/test/slugged_model_test.rb +++ b/test/slugged_model_test.rb @@ -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 diff --git a/test/sti_test.rb b/test/sti_test.rb index 46bc3d317..e0c2f98eb 100644 --- a/test/sti_test.rb +++ b/test/sti_test.rb @@ -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