dougcole-friendly_id 2.0.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (44) hide show
  1. data/History.txt +88 -0
  2. data/MIT-LICENSE +19 -0
  3. data/Manifest.txt +45 -0
  4. data/README.rdoc +318 -0
  5. data/Rakefile +67 -0
  6. data/generators/friendly_id/friendly_id_generator.rb +12 -0
  7. data/generators/friendly_id/templates/create_slugs.rb +18 -0
  8. data/generators/friendly_id_20_upgrade/friendly_id_20_upgrade_generator.rb +11 -0
  9. data/generators/friendly_id_20_upgrade/templates/upgrade_friendly_id_to_20.rb +19 -0
  10. data/init.rb +1 -0
  11. data/lib/friendly_id/helpers.rb +13 -0
  12. data/lib/friendly_id/non_sluggable_class_methods.rb +40 -0
  13. data/lib/friendly_id/non_sluggable_instance_methods.rb +33 -0
  14. data/lib/friendly_id/shoulda_macros.rb +36 -0
  15. data/lib/friendly_id/slug.rb +89 -0
  16. data/lib/friendly_id/sluggable_class_methods.rb +103 -0
  17. data/lib/friendly_id/sluggable_instance_methods.rb +106 -0
  18. data/lib/friendly_id/version.rb +8 -0
  19. data/lib/friendly_id.rb +67 -0
  20. data/lib/tasks/friendly_id.rake +48 -0
  21. data/lib/tasks/friendly_id.rb +1 -0
  22. data/test/database.yml +3 -0
  23. data/test/fixtures/countries.yml +4 -0
  24. data/test/fixtures/country.rb +4 -0
  25. data/test/fixtures/people.yml +7 -0
  26. data/test/fixtures/person.rb +6 -0
  27. data/test/fixtures/post.rb +3 -0
  28. data/test/fixtures/posts.yml +23 -0
  29. data/test/fixtures/slugs.yml +53 -0
  30. data/test/fixtures/user.rb +3 -0
  31. data/test/fixtures/users.yml +7 -0
  32. data/test/non_slugged_test.rb +85 -0
  33. data/test/rails/2.x/app/controllers/application.rb +0 -0
  34. data/test/rails/2.x/config/boot.rb +109 -0
  35. data/test/rails/2.x/config/database.yml +3 -0
  36. data/test/rails/2.x/config/environment.rb +7 -0
  37. data/test/rails/2.x/config/environments/test.rb +6 -0
  38. data/test/rails/2.x/config/routes.rb +0 -0
  39. data/test/schema.rb +38 -0
  40. data/test/scoped_model_test.rb +21 -0
  41. data/test/slug_test.rb +87 -0
  42. data/test/sluggable_test.rb +185 -0
  43. data/test/test_helper.rb +35 -0
  44. metadata +157 -0
@@ -0,0 +1,33 @@
1
+ module FriendlyId::NonSluggableInstanceMethods
2
+
3
+ attr :found_using_friendly_id
4
+
5
+ # Was the record found using one of its friendly ids?
6
+ def found_using_friendly_id?
7
+ @found_using_friendly_id
8
+ end
9
+
10
+ # Was the record found using its numeric id?
11
+ def found_using_numeric_id?
12
+ !@found_using_friendly_id
13
+ end
14
+ alias has_better_id? found_using_numeric_id?
15
+
16
+ # Returns the friendly_id.
17
+ def friendly_id
18
+ send friendly_id_options[:column]
19
+ end
20
+ alias best_id friendly_id
21
+
22
+ # Returns the friendly id, or if none is available, the numeric id.
23
+ def to_param
24
+ friendly_id.to_s || id.to_s
25
+ end
26
+
27
+ private
28
+
29
+ def found_using_friendly_id=(value) #:nodoc#
30
+ @found_using_friendly_id = value
31
+ end
32
+
33
+ end
@@ -0,0 +1,36 @@
1
+ module FriendlyId
2
+
3
+ # A Shoulda[http://www.thoughtbot.com/projects/shoulda/] macros for testing
4
+ # models using FriendlyId.
5
+ module ShouldaMacros
6
+
7
+ # Ensure that a model is using FriendlyId.
8
+ def self.should_have_friendly_id(column, options = {})
9
+
10
+ options.assert_valid_keys(:use_slug)
11
+ klass = self.model_class
12
+
13
+ should "have friendly id for #{method}" do
14
+ assert_respond_to klass, :friendly_id_options,
15
+ "#{klass} does not respond to friendly_id_options"
16
+ assert_equal column, klass.friendly_id_options[:method]
17
+ end
18
+
19
+ if options[:use_slug]
20
+ should "include/extend friendly_id's sluggable modules" do
21
+ assert klass.extended_by.include?(FriendlyId::SluggableClassMethods),
22
+ "#{klass} does not extend FriendlyId::SluggableClassMethods"
23
+ assert klass.include?(FriendlyId::SluggableInstanceMethods),
24
+ "#{klass} not include FriendlyId::SluggableInstanceMethods"
25
+ end
26
+ else
27
+ should "include/extend friendly_id's non-sluggable modules" do
28
+ assert klass.extended_by.include?(FriendlyId::NonSluggableClassMethods),
29
+ "#{klass} does not extend FriendlyId::NonSluggableClassMethods"
30
+ assert klass.include?(FriendlyId::NonSluggableInstanceMethods),
31
+ "#{klass} not include FriendlyId::NonSluggableInstanceMethods"
32
+ end
33
+ end
34
+ end
35
+ end
36
+ end
@@ -0,0 +1,89 @@
1
+ # A Slug is a unique, human-friendly identifier for an ActiveRecord.
2
+ class Slug < ActiveRecord::Base
3
+
4
+ belongs_to :sluggable, :polymorphic => true
5
+ before_save :check_for_blank_name, :set_sequence
6
+
7
+ class << self
8
+
9
+ # Sanitizes and dasherizes string to make it safe for URL's.
10
+ #
11
+ # Example:
12
+ #
13
+ # slug.normalize('This... is an example!') # => "this-is-an-example"
14
+ #
15
+ # Note that Rails 2.2.x offers a parameterize method for this. It's not
16
+ # used here because it assumes you want to strip away accented characters,
17
+ # and this may not always be your desire.
18
+ #
19
+ # At the time of writing, it also handles several characters incorrectly,
20
+ # for instance replacing Icelandic's "thorn" character with "y" rather
21
+ # than "d." This might be pedantic, but I don't want to piss off the
22
+ # Vikings. The last time anyone pissed them off, they uleashed a wave of
23
+ # terror in Europe unlike anything ever seen before or after. I'm not
24
+ # taking any chances.
25
+ def normalize(slug_text)
26
+ return "" if slug_text.blank?
27
+ slug_text.
28
+ send(chars_func).
29
+ # For some reason Spanish ¡ and ¿ are not detected as non-word
30
+ # characters. Bug in Ruby?
31
+ normalize.gsub(/[\W|¡|¿]/u, ' ').
32
+ strip.
33
+ gsub(/\s+/u, '-').
34
+ gsub(/-\z/u, '').
35
+ downcase.
36
+ to_s
37
+ end
38
+
39
+ def parse(friendly_id)
40
+ name, sequence = friendly_id.split('--')
41
+ sequence ||= "1"
42
+ return name, sequence
43
+ end
44
+
45
+ # Remove diacritics (accents, umlauts, etc.) from the string.
46
+ def strip_diacritics(string)
47
+ require 'unicode'
48
+ Unicode::normalize_KD(string).unpack('U*').select { |cp|
49
+ cp < 0x300 || cp > 0x036F
50
+ }.pack('U*')
51
+ end
52
+
53
+ private
54
+
55
+ def chars_func
56
+ Rails.version =~ /2.2.[\d]*/ ? :mb_chars : :chars
57
+ rescue NoMethodError
58
+ :chars
59
+ end
60
+
61
+ end
62
+
63
+ # Whether or not this slug is the most recent of its owner's slugs.
64
+ def is_most_recent?
65
+ sluggable.slug == self
66
+ end
67
+
68
+ def to_friendly_id
69
+ sequence > 1 ? "#{name}--#{sequence}" : name
70
+ end
71
+
72
+ protected
73
+
74
+ # Raise a FriendlyId::SlugGenerationError if the slug name is blank.
75
+ def check_for_blank_name #:nodoc:#
76
+ if name.blank?
77
+ raise FriendlyId::SlugGenerationError.new("The slug text is blank.")
78
+ end
79
+ end
80
+
81
+ def set_sequence
82
+ return unless new_record?
83
+ last = Slug.find(:first, :conditions => { :name => name, :scope => scope,
84
+ :sluggable_type => sluggable_type}, :order => "sequence DESC",
85
+ :select => 'sequence')
86
+ self.sequence = last.sequence + 1 if last
87
+ end
88
+
89
+ end
@@ -0,0 +1,103 @@
1
+ module FriendlyId::SluggableClassMethods
2
+
3
+ include FriendlyId::Helpers
4
+
5
+ def self.extended(base) #:nodoc:#
6
+
7
+ class << base
8
+ alias_method_chain :find_one, :friendly
9
+ alias_method_chain :find_some, :friendly
10
+ alias_method_chain :validate_find_options, :friendly
11
+ end
12
+
13
+ end
14
+
15
+ # Finds a single record using the friendly id, or the record's id.
16
+ def find_one_with_friendly(id_or_name, options) #:nodoc:#
17
+
18
+ scope = options.delete(:scope)
19
+ return find_one_without_friendly(id_or_name, options) if id_or_name.is_a?(Fixnum)
20
+
21
+ find_options = {:select => "#{self.table_name}.*"}
22
+ find_options[:joins] = :slugs unless options[:include] && [*options[:include]].flatten.include?(:slugs)
23
+
24
+ name, sequence = Slug.parse(id_or_name)
25
+
26
+ find_options[:conditions] = {
27
+ "#{Slug.table_name}.name" => name,
28
+ "#{Slug.table_name}.scope" => scope,
29
+ "#{Slug.table_name}.sequence" => sequence
30
+ }
31
+
32
+ result = with_scope(:find => find_options) { find_initial(options) }
33
+
34
+ if result
35
+ result.finder_slug_name = id_or_name
36
+ else
37
+ result = find_one_without_friendly id_or_name, options
38
+ end
39
+
40
+ result
41
+
42
+ end
43
+
44
+ # Finds multiple records using the friendly ids, or the records' ids.
45
+ def find_some_with_friendly(ids_and_names, options) #:nodoc:#
46
+
47
+ slugs, ids = get_slugs_and_ids(ids_and_names, options)
48
+ results = []
49
+
50
+ find_options = {:select => "#{self.table_name}.*"}
51
+ find_options[:joins] = :slugs unless options[:include] && [*options[:include]].flatten.include?(:slugs)
52
+ find_options[:conditions] = "#{quoted_table_name}.#{primary_key} IN (#{ids.empty? ? 'NULL' : ids.join(',')}) "
53
+ find_options[:conditions] << "OR slugs.id IN (#{slugs.to_s(:db)})"
54
+
55
+ results = with_scope(:find => find_options) { find_every(options) }
56
+
57
+ expected = expected_size(ids_and_names, options)
58
+ if results.size != expected
59
+ raise ActiveRecord::RecordNotFound, "Couldn't find all #{ name.pluralize } with IDs (#{ ids_and_names * ', ' }) AND #{ sanitize_sql options[:conditions] } (found #{ results.size } results, but was looking for #{ expected })"
60
+ end
61
+
62
+ assign_finder_slugs(slugs, results)
63
+
64
+ results
65
+ end
66
+
67
+ def validate_find_options_with_friendly(options) #:nodoc:#
68
+ options.assert_valid_keys([:conditions, :include, :joins, :limit, :offset,
69
+ :order, :select, :readonly, :group, :from, :lock, :having, :scope])
70
+ end
71
+
72
+ private
73
+
74
+ # Assign finder slugs for the results found in find_some_with_friendly
75
+ def assign_finder_slugs(slugs, results) #:nodoc:#
76
+ slugs.each do |slug|
77
+ results.select { |r| r.id == slug.sluggable_id }.each do |result|
78
+ result.send(:finder_slug=, slug)
79
+ end
80
+ end
81
+ end
82
+
83
+ # Build arrays of slugs and ids, for the find_some_with_friendly method.
84
+ def get_slugs_and_ids(ids_and_names, options) #:nodoc:#
85
+ scope = options.delete(:scope)
86
+ slugs = []
87
+ ids = []
88
+ ids_and_names.each do |id_or_name|
89
+ name, sequence = Slug.parse id_or_name
90
+ slug = Slug.find(:first, :readonly => true, :conditions => {
91
+ :name => name,
92
+ :scope => scope,
93
+ :sequence => sequence,
94
+ :sluggable_type => base_class.name
95
+ })
96
+ # If the slug was found, add it to the array for later use. If not, and
97
+ # the id_or_name is a number, assume that it is a regular record id.
98
+ slug ? slugs << slug : (ids << id_or_name if id_or_name =~ /\A\d*\z/)
99
+ end
100
+ return slugs, ids
101
+ end
102
+
103
+ end
@@ -0,0 +1,106 @@
1
+ module FriendlyId::SluggableInstanceMethods
2
+
3
+ NUM_CHARS_RESERVED_FOR_FRIENDLY_ID_EXTENSION = 2
4
+
5
+ attr :finder_slug
6
+ attr_accessor :finder_slug_name
7
+
8
+ def finder_slug
9
+ @finder_slug ||= init_finder_slug
10
+ end
11
+
12
+ # Was the record found using one of its friendly ids?
13
+ def found_using_friendly_id?
14
+ finder_slug
15
+ end
16
+
17
+ # Was the record found using its numeric id?
18
+ def found_using_numeric_id?
19
+ !found_using_friendly_id?
20
+ end
21
+
22
+ # Was the record found using an old friendly id?
23
+ def found_using_outdated_friendly_id?
24
+ finder_slug.id != slug.id
25
+ end
26
+
27
+ # Was the record found using an old friendly id, or its numeric id?
28
+ def has_better_id?
29
+ slug and found_using_numeric_id? || found_using_outdated_friendly_id?
30
+ end
31
+
32
+ # Returns the friendly id.
33
+ def friendly_id
34
+ slug(true).to_friendly_id
35
+ end
36
+ alias best_id friendly_id
37
+
38
+ # Has the basis of our friendly id changed, requiring the generation of a
39
+ # new slug?
40
+ def new_slug_needed?
41
+ !slug || slug_text != slug.name
42
+ end
43
+
44
+ # Returns the most recent slug, which is used to determine the friendly
45
+ # id.
46
+ def slug(reload = false)
47
+ @most_recent_slug = nil if reload
48
+ @most_recent_slug ||= slugs.first
49
+ end
50
+
51
+ # Returns the friendly id, or if none is available, the numeric id.
52
+ def to_param
53
+ slug ? slug.to_friendly_id : id.to_s
54
+ end
55
+
56
+ # Get the processed string used as the basis of the friendly id.
57
+ def slug_text
58
+ base = send friendly_id_options[:column]
59
+ if self.friendly_id_options[:strip_diacritics]
60
+ base = Slug::normalize(Slug::strip_diacritics(base))
61
+ else
62
+ base = Slug::normalize(base)
63
+ end
64
+ if base.length > friendly_id_options[:max_length]
65
+ base = base[0...friendly_id_options[:max_length]]
66
+ end
67
+ if friendly_id_options[:reserved].include?(base)
68
+ raise FriendlyId::SlugGenerationError.new("The slug text is a reserved value")
69
+ end
70
+ return base
71
+ end
72
+
73
+ private
74
+
75
+ def finder_slug=(finder_slug)
76
+ @finder_slug_name = finder_slug.name
77
+ slug = finder_slug
78
+ slug.sluggable = self
79
+ slug
80
+ end
81
+
82
+ def init_finder_slug
83
+ return false if !@finder_slug_name
84
+ name, sequence = Slug.parse(@finder_slug_name)
85
+ slug = Slug.find(:first, :conditions => {:sluggable_id => id, :name => name, :sequence => sequence, :sluggable_type => self.class.name })
86
+ finder_slug = slug
87
+ end
88
+
89
+ # Set the slug using the generated friendly id.
90
+ def set_slug
91
+ if self.class.friendly_id_options[:use_slug] && new_slug_needed?
92
+ @most_recent_slug = nil
93
+ slug_attributes = {:name => slug_text}
94
+ if friendly_id_options[:scope]
95
+ scope = send(friendly_id_options[:scope])
96
+ slug_attributes[:scope] = scope.respond_to?(:to_param) ? scope.to_param : scope.to_s
97
+ end
98
+ # If we're renaming back to a previously used friendly_id, delete the
99
+ # slug so that we can recycle the name without having to use a sequence.
100
+ slugs.find(:all, :conditions => {:name => slug_text, :scope => scope}).each { |s| s.destroy }
101
+ slug = slugs.build slug_attributes
102
+ slug
103
+ end
104
+ end
105
+
106
+ end
@@ -0,0 +1,8 @@
1
+ module FriendlyId #:nodoc:
2
+ module Version #:nodoc:
3
+ MAJOR = 2
4
+ MINOR = 0
5
+ TINY = 1
6
+ STRING = [MAJOR, MINOR, TINY].join('.')
7
+ end
8
+ end
@@ -0,0 +1,67 @@
1
+ require 'unicode'
2
+ require 'friendly_id/helpers'
3
+ require 'friendly_id/slug'
4
+ require 'friendly_id/shoulda_macros'
5
+
6
+
7
+ # FriendlyId is a comprehensize Rails plugin/gem for slugging and permalinks.
8
+ module FriendlyId
9
+
10
+ # This error is raised when it's not possible to generate a unique slug.
11
+ class SlugGenerationError < StandardError ; end
12
+
13
+ module ClassMethods
14
+
15
+ # Default options for friendly_id.
16
+ DEFAULT_FRIENDLY_ID_OPTIONS = {:method => nil, :use_slug => false, :max_length => 255, :reserved => [], :strip_diacritics => false, :scope => nil}.freeze
17
+ VALID_FRIENDLY_ID_KEYS = [:use_slug, :max_length, :reserved, :strip_diacritics, :scope].freeze
18
+
19
+ # Set up an ActiveRecord model to use a friendly_id.
20
+ #
21
+ # The column argument can be one of your model's columns, or a method
22
+ # you use to generate the slug.
23
+ #
24
+ # Options:
25
+ # * <tt>:use_slug</tt> - Defaults to false. Use slugs when you want to use a non-unique text field for friendly ids.
26
+ # * <tt>:max_length</tt> - Defaults to 255. The maximum allowed length for a slug.
27
+ # * <tt>:strip_diacritics</tt> - Defaults to false. If true, it will remove accents, umlauts, etc. from western characters.
28
+ # * <tt>:reseved</tt> - Array of words that are reserved and can't be used as slugs. If such a word is used, it will be treated the same as if that slug was already taken (numeric extension will be appended). Defaults to [].
29
+ def has_friendly_id(column, options = {})
30
+ options.assert_valid_keys VALID_FRIENDLY_ID_KEYS
31
+ options = DEFAULT_FRIENDLY_ID_OPTIONS.merge(options).merge(:column => column)
32
+ write_inheritable_attribute :friendly_id_options, options
33
+ class_inheritable_reader :friendly_id_options
34
+
35
+ if options[:use_slug]
36
+ has_many :slugs, :order => 'id DESC', :as => :sluggable, :dependent => :destroy, :readonly => true
37
+ require 'friendly_id/sluggable_class_methods'
38
+ require 'friendly_id/sluggable_instance_methods'
39
+ extend SluggableClassMethods
40
+ include SluggableInstanceMethods
41
+ before_save :set_slug
42
+ else
43
+ require 'friendly_id/non_sluggable_class_methods'
44
+ require 'friendly_id/non_sluggable_instance_methods'
45
+ extend NonSluggableClassMethods
46
+ include NonSluggableInstanceMethods
47
+ end
48
+ end
49
+
50
+ end
51
+
52
+ class << self
53
+
54
+ # Load FriendlyId if the gem is included in a Rails app.
55
+ def enable
56
+ return if ActiveRecord::Base.methods.include? 'has_friendly_id'
57
+ ActiveRecord::Base.class_eval { extend FriendlyId::ClassMethods }
58
+ Test::Unit::TestCase.class_eval { include FriendlyId::ShouldaMacros }
59
+ end
60
+
61
+ end
62
+
63
+ end
64
+
65
+ if defined?(ActiveRecord)
66
+ FriendlyId::enable
67
+ end
@@ -0,0 +1,48 @@
1
+ namespace :friendly_id do
2
+ desc "Make slugs for a model."
3
+ task :make_slugs => :environment do
4
+ raise 'USAGE: rake friendly_id:make_slugs MODEL=MyModelName' if ENV["MODEL"].nil?
5
+ if !sluggable_class.friendly_id_options[:use_slug]
6
+ raise "Class \"#{sluggable_class.to_s}\" doesn't appear to be using slugs"
7
+ end
8
+ while records = sluggable_class.find(:all, :include => :slugs, :conditions => "slugs.id IS NULL", :limit => 1000) do
9
+ break if records.size == 0
10
+ records.each do |r|
11
+ r.send(:set_slug)
12
+ r.save!
13
+ puts "#{sluggable_class.to_s}(#{r.id}) friendly_id set to \"#{r.slug.name}\""
14
+ end
15
+ end
16
+ end
17
+
18
+ desc "Regenereate slugs for a model."
19
+ task :redo_slugs => :environment do
20
+ raise 'USAGE: rake friendly_id:redo_slugs MODEL=MyModelName' if ENV["MODEL"].nil?
21
+ if !sluggable_class.friendly_id_options[:use_slug]
22
+ raise "Class \"#{sluggable_class.to_s}\" doesn't appear to be using slugs"
23
+ end
24
+ Slug.destroy_all(["sluggable_type = ?", sluggable_class.to_s])
25
+ Rake::Task["friendly_id:make_slugs"].invoke
26
+ end
27
+
28
+ desc "Kill obsolete slugs older than 45 days."
29
+ task :remove_old_slugs => :environment do
30
+ if ENV["DAYS"].nil?
31
+ @days = 45
32
+ else
33
+ @days = ENV["DAYS"].to_i
34
+ end
35
+ slugs = Slug.find(:all, :conditions => ["created_at < ?", DateTime.now - @days.days])
36
+ slugs.each do |s|
37
+ s.destroy if !s.is_most_recent?
38
+ end
39
+ end
40
+ end
41
+
42
+ def sluggable_class
43
+ if (ENV["MODEL"].split('::').size > 1)
44
+ ENV["MODEL"].split('::').inject(Kernel) {|scope, const_name| scope.const_get(const_name)}
45
+ else
46
+ Object.const_get(ENV["MODEL"])
47
+ end
48
+ end
@@ -0,0 +1 @@
1
+ load 'tasks/friendly_id.rake'
data/test/database.yml ADDED
@@ -0,0 +1,3 @@
1
+ sqlite3:
2
+ adapter: sqlite3
3
+ database: ":memory:"
@@ -0,0 +1,4 @@
1
+ argentina:
2
+ name: Argentina
3
+ usa:
4
+ name: USA
@@ -0,0 +1,4 @@
1
+ class Country < ActiveRecord::Base
2
+ has_many :people
3
+ has_friendly_id :name, :use_slug => true
4
+ end
@@ -0,0 +1,7 @@
1
+ john_smith:
2
+ name: John Smith
3
+ country: argentina
4
+
5
+ john_smith2:
6
+ name: John Smith
7
+ country: usa
@@ -0,0 +1,6 @@
1
+ class Person < ActiveRecord::Base
2
+
3
+ belongs_to :country
4
+ has_friendly_id :name, :use_slug => true, :scope => :country
5
+
6
+ end
@@ -0,0 +1,3 @@
1
+ class Post < ActiveRecord::Base
2
+ has_friendly_id :name, :use_slug => true, :reserved => ['new', 'edit']
3
+ end
@@ -0,0 +1,23 @@
1
+ without_slug:
2
+ name: Without a slug
3
+ content: Content without a slug
4
+
5
+ with_one_slug:
6
+ name: With one slug
7
+ content: Content with one slug
8
+
9
+ with_two_slugs:
10
+ name: With two slugs
11
+ content: Content with two slugs
12
+
13
+ common_title:
14
+ name: Common Title
15
+ content: A post with a very common title
16
+
17
+ common_title2:
18
+ name: Common Title
19
+ content: A second post with a very common title
20
+
21
+ john_smith:
22
+ name: John Smith
23
+ content: Should allow for identical slug names between sluggable types
@@ -0,0 +1,53 @@
1
+ post_with_same_friendly_id_as_person:
2
+ name: john-smith
3
+ sluggable: john_smith (Post)
4
+
5
+ person_with_same_friendly_id_as_post:
6
+ name: john-smith
7
+ sluggable: john_smith (Person)
8
+
9
+ one:
10
+ name: with-one-slug
11
+ sluggable: with_one_slug
12
+ sluggable_type: Post
13
+ sequence: 1
14
+
15
+ two_old:
16
+ name: with-two-slugs
17
+ sluggable: with_two_slugs (Post)
18
+ sequence: 1
19
+
20
+ two_new:
21
+ name: with-two-slugs-new
22
+ sluggable: with_two_slugs (Post)
23
+ sequence: 1
24
+
25
+ common_title:
26
+ name: common-title
27
+ sluggable: common_title (Post)
28
+ sequence: 1
29
+
30
+ common_title2:
31
+ name: common-title
32
+ sluggable: common_title2 (Post)
33
+ sequence: 2
34
+
35
+ john_smith:
36
+ name: john-smith
37
+ sluggable: john_smith (Person)
38
+ sequence: 1
39
+ scope: argentina
40
+
41
+ john_smith2:
42
+ name: john-smith
43
+ sluggable: john_smith2 (Person)
44
+ sequence: 1
45
+ scope: usa
46
+
47
+ argentina:
48
+ name: argentina
49
+ sluggable: argentina (Country)
50
+
51
+ usa:
52
+ name: usa
53
+ sluggable: usa (Country)
@@ -0,0 +1,3 @@
1
+ class User < ActiveRecord::Base
2
+ has_friendly_id :login
3
+ end
@@ -0,0 +1,7 @@
1
+ joe:
2
+ login: joe
3
+ email: joe@example.org
4
+
5
+ jane:
6
+ login: jane
7
+ email: jane@example.org