friendly_id 4.0.0 → 4.0.1

Sign up to get free protection for your applications and to get access to all the features.
data/README.md CHANGED
@@ -3,10 +3,10 @@
3
3
  [![Build Status](http://travis-ci.org/norman/friendly_id.png)](http://travis-ci.org/norman/friendly_id)
4
4
 
5
5
  FriendlyId is the "Swiss Army bulldozer" of slugging and permalink plugins for
6
- Ruby on Rails. It allows you to create pretty URL's and work with human-friendly
6
+ Ruby on Rails. It allows you to create pretty URLs and work with human-friendly
7
7
  strings as if they were numeric ids for Active Record models.
8
8
 
9
- Using FriendlyId, it's easy to make your application use URL's like:
9
+ Using FriendlyId, it's easy to make your application use URLs like:
10
10
 
11
11
  http://example.com/states/washington
12
12
 
@@ -18,9 +18,10 @@ instead of:
18
18
  ## FriendlyId Features
19
19
 
20
20
  FriendlyId offers many advanced features, including: slug history and
21
- versioning, i18n, scoped slugs, reserved words, and custom slug generators.
21
+ versioning, i18n, Globalize support, scoped slugs, reserved words, and custom
22
+ slug generators.
22
23
 
23
- FriendlyId is compatible with Active Record **3.0**, **3.1** and **3.2**
24
+ FriendlyId is compatible with Active Record **3.0** and higher.
24
25
 
25
26
  ## Version 4.x
26
27
 
@@ -38,6 +39,8 @@ The best place to start is with the
38
39
  [Guide](http://rubydoc.info/github/norman/friendly_id/master/file/Guide.rdoc),
39
40
  which compiles the top-level RDocs into one outlined document.
40
41
 
42
+ You might also want to watch Ryan Bates's [Railscast on FriendlyId](http://railscasts.com/episodes/314-pretty-urls-with-friendlyid).
43
+
41
44
  ## Rails Quickstart
42
45
 
43
46
  gem install friendly_id
@@ -46,22 +49,22 @@ which compiles the top-level RDocs into one outlined document.
46
49
 
47
50
  cd my_app
48
51
 
49
- gem "friendly_id", "~> 4.0.0"
52
+ gem "friendly_id", "~> 4.0.1"
50
53
 
51
54
  rails generate scaffold user name:string slug:string
52
55
 
53
56
  # edit db/migrate/*_create_users.rb
54
- add_index :users, :slug, :unique => true
57
+ add_index :users, :slug, unique: true
55
58
 
56
59
  rake db:migrate
57
60
 
58
61
  # edit app/models/user.rb
59
62
  class User < ActiveRecord::Base
60
63
  extend FriendlyId
61
- friendly_id :name, :use => :slugged
64
+ friendly_id :name, use: :slugged
62
65
  end
63
66
 
64
- User.create! :name => "Joe Schmoe"
67
+ User.create! name: "Joe Schmoe"
65
68
 
66
69
  rails server
67
70
 
@@ -87,13 +90,16 @@ tracker](http://github.com/norman/friendly_id/issues) for this project.
87
90
  If you have a bug to report, please include the following information:
88
91
 
89
92
  * **Version information for FriendlyId, Rails and Ruby.**
90
- * Stack trace and error message.
93
+ * Full stack trace and error message (if you have them).
91
94
  * Any snippets of relevant model, view or controller code that shows how you
92
95
  are using FriendlyId.
93
96
 
94
97
  If you are able to, it helps even more if you can fork FriendlyId on Github,
95
98
  and add a test that reproduces the error you are experiencing.
96
99
 
100
+ For more info on how to report bugs, please see [this
101
+ article](http://yourbugreportneedsmore.info/).
102
+
97
103
  ## Thanks and Credits
98
104
 
99
105
  FriendlyId was originally created by Norman Clarke and Adrian Mugnolo, with
@@ -105,7 +111,7 @@ Part of the inspiration to rework FriendlyId came from Darcy Laycock's library
105
111
  [Slugged](https://github.com/Sutto/slugged), which he was inspired to create
106
112
  because of frustrations he experienced while using FriendlyId 3.x. Seeing a
107
113
  smart programmer become frustrated with my code was enough of a kick in the
108
- butt to make me want to improve this library significantly.
114
+ butt to make me want to significantly improve this library.
109
115
 
110
116
  Many thanks to him for providing valid, real criticism while still being a cool
111
117
  about it. I definitely recommend you check out his library if for some reason
@@ -122,7 +128,7 @@ should be!
122
128
 
123
129
  ## License
124
130
 
125
- Copyright (c) 2008-2011 Norman Clarke, released under the MIT license.
131
+ Copyright (c) 2008-2012 Norman Clarke, released under the MIT license.
126
132
 
127
133
  Permission is hereby granted, free of charge, to any person obtaining a copy of
128
134
  this software and associated documentation files (the "Software"), to deal in
data/Rakefile CHANGED
@@ -68,7 +68,7 @@ namespace :db do
68
68
  driver = FriendlyId::Test::Database.driver
69
69
  config = FriendlyId::Test::Database.config[driver]
70
70
  commands = {
71
- "mysql" => "mysql -e 'create database #{config["database"]};' >/dev/null",
71
+ "mysql" => "mysql -u #{config['username']} -e 'create database #{config["database"]};' >/dev/null",
72
72
  "postgres" => "psql -c 'create database #{config['database']};' -U #{config['username']} >/dev/null"
73
73
  }
74
74
  %x{#{commands[driver] || true}}
@@ -80,7 +80,7 @@ namespace :db do
80
80
  driver = FriendlyId::Test::Database.driver
81
81
  config = FriendlyId::Test::Database.config[driver]
82
82
  commands = {
83
- "mysql" => "mysql -e 'drop database #{config["database"]};' >/dev/null",
83
+ "mysql" => "mysql -u #{config['username']} -e 'drop database #{config["database"]};' >/dev/null",
84
84
  "postgres" => "psql -c 'drop database #{config['database']};' -U #{config['username']} >/dev/null"
85
85
  }
86
86
  %x{#{commands[driver] || true}}
data/friendly_id.gemspec CHANGED
@@ -8,28 +8,27 @@ Gem::Specification.new do |s|
8
8
  s.version = FriendlyId::VERSION
9
9
  s.authors = ["Norman Clarke"]
10
10
  s.email = ["norman@njclarke.com"]
11
- s.homepage = "http://norman.github.com/friendly_id"
11
+ s.homepage = "http://github.com/norman/friendly_id"
12
12
  s.summary = "A comprehensive slugging and pretty-URL plugin."
13
13
  s.rubyforge_project = "friendly_id"
14
14
  s.files = `git ls-files`.split("\n")
15
15
  s.test_files = `git ls-files -- {test}/*`.split("\n")
16
16
  s.require_paths = ["lib"]
17
17
 
18
- s.add_development_dependency "railties", "~> 3.1.0"
19
- s.add_development_dependency "activerecord", "~> 3.1.0"
20
- s.add_development_dependency "sqlite3", "~> 1.3.4"
21
- s.add_development_dependency "minitest", "~> 2.4.0"
22
- s.add_development_dependency "mocha", "~> 0.9.12"
23
- s.add_development_dependency "ffaker", "~> 1.8.0"
24
- s.add_development_dependency "maruku", "~> 0.6.0"
25
- s.add_development_dependency "yard", "~> 0.7.2"
26
- s.add_development_dependency "i18n", "~> 0.6.0"
18
+ s.add_development_dependency "railties", "~> 3.2.0"
19
+ s.add_development_dependency "activerecord", "~> 3.2.0"
20
+ s.add_development_dependency "minitest"
21
+ s.add_development_dependency "mocha"
22
+ s.add_development_dependency "maruku"
23
+ s.add_development_dependency "yard"
24
+ s.add_development_dependency "i18n"
25
+ s.add_development_dependency "ffaker"
27
26
  s.add_development_dependency "simplecov"
28
27
 
29
28
  s.description = <<-EOM
30
29
  FriendlyId is the "Swiss Army bulldozer" of slugging and permalink plugins for
31
- Ruby on Rails. It allows you to create pretty URL's and work with
32
- human-friendly strings as if they were numeric ids for ActiveRecord models.
30
+ Ruby on Rails. It allows you to create pretty URLs and work with human-friendly
31
+ strings as if they were numeric ids for Active Record models.
33
32
  EOM
34
33
 
35
34
  s.post_install_message = <<-EOM
@@ -1,19 +1,20 @@
1
1
  source :rubygems
2
2
 
3
- # platform :jruby do
4
- # gem "activerecord-jdbcmysql-adapter"
5
- # gem "activerecord-jdbcpostgresql-adapter"
6
- # gem "activerecord-jdbcsqlite3-adapter"
7
- # end
3
+ platforms :jruby do
4
+ gem 'activerecord-jdbcsqlite3-adapter'
5
+ gem 'activerecord-jdbcmysql-adapter'
6
+ gem 'activerecord-jdbcpostgresql-adapter'
7
+ gem 'jruby-openssl'
8
+ end
8
9
 
9
- platform :ruby do
10
- gem "mysql2", "~> 0.2.0"
11
- gem "pg", "~> 0.11.0"
12
- gem "sqlite3", "~> 1.3.4"
10
+ platforms :ruby do
11
+ gem 'sqlite3'
12
+ gem 'mysql2', '~> 0.2.0'
13
+ gem 'pg'
13
14
  end
14
15
 
15
- gem "activerecord", "~> 3.0.10"
16
- gem "minitest", "~> 2.4.0"
17
- gem "mocha", "~> 0.9.12"
18
- gem "railties", "~> 3.0.10"
19
- gem "rake"
16
+ gem 'activerecord', '~> 3.0.0'
17
+ gem 'railties', '~> 3.0.0'
18
+ gem 'minitest'
19
+ gem 'mocha'
20
+ gem 'rake'
@@ -1,21 +1,21 @@
1
1
  source :rubygems
2
2
 
3
- # platform :jruby do
4
- # gem "activerecord-jdbc-adapter", :git => "https://github.com/nicksieger/activerecord-jdbc-adapter.git"
5
- # gem "activerecord-jdbcmysql-adapter"
6
- # gem "activerecord-jdbcpostgresql-adapter"
7
- # gem "activerecord-jdbcsqlite3-adapter"
8
- # end
3
+ platforms :jruby do
4
+ gem 'activerecord-jdbcsqlite3-adapter'
5
+ gem 'activerecord-jdbcmysql-adapter'
6
+ gem 'activerecord-jdbcpostgresql-adapter'
7
+ gem 'jruby-openssl'
8
+ end
9
9
 
10
- platform :ruby do
11
- gem "mysql2", "~> 0.3.6"
12
- gem "pg", "~> 0.11.0"
13
- gem "sqlite3", "~> 1.3.4"
10
+ platforms :ruby do
11
+ gem 'sqlite3'
12
+ gem 'mysql2'
13
+ gem 'pg'
14
14
  end
15
15
 
16
- gem "ffaker"
17
- gem "activerecord", "~> 3.1.3"
18
- gem "minitest", "~> 2.4.0"
19
- gem "mocha", "~> 0.9.12"
20
- gem "railties", "~> 3.1.3"
21
- gem "rake"
16
+ gem 'activerecord', '~> 3.1.0'
17
+ gem 'railties', '~> 3.1.3'
18
+ gem 'ffaker'
19
+ gem 'minitest'
20
+ gem 'mocha'
21
+ gem 'rake'
@@ -1,21 +1,21 @@
1
1
  source :rubygems
2
2
 
3
- # platform :jruby do
4
- # gem "activerecord-jdbc-adapter", :git => "https://github.com/nicksieger/activerecord-jdbc-adapter.git"
5
- # gem "activerecord-jdbcmysql-adapter"
6
- # gem "activerecord-jdbcpostgresql-adapter"
7
- # gem "activerecord-jdbcsqlite3-adapter"
8
- # end
3
+ platforms :jruby do
4
+ gem 'activerecord-jdbcsqlite3-adapter'
5
+ gem 'activerecord-jdbcmysql-adapter'
6
+ gem 'activerecord-jdbcpostgresql-adapter'
7
+ gem 'jruby-openssl'
8
+ end
9
9
 
10
- platform :ruby do
11
- gem "mysql2", "~> 0.3.6"
12
- gem "pg", "~> 0.11.0"
13
- gem "sqlite3", "~> 1.3.4"
10
+ platforms :ruby do
11
+ gem 'sqlite3'
12
+ gem 'mysql2'
13
+ gem 'pg'
14
14
  end
15
15
 
16
- gem "ffaker"
17
- gem "activerecord", "~> 3.2.0.rc1"
18
- gem "minitest", "~> 2.4.0"
19
- gem "mocha", "~> 0.9.12"
20
- gem "railties", "~> 3.2.0.rc1"
21
- gem "rake"
16
+ gem 'ffaker'
17
+ gem 'activerecord', '~> 3.2.0'
18
+ gem 'railties', '~> 3.2.0'
19
+ gem 'minitest'
20
+ gem 'mocha'
21
+ gem 'rake'
data/lib/friendly_id.rb CHANGED
@@ -45,7 +45,7 @@ with numeric ids:
45
45
  module FriendlyId
46
46
 
47
47
  # The current version.
48
- VERSION = "4.0.0"
48
+ VERSION = "4.0.1"
49
49
 
50
50
  @mutex = Mutex.new
51
51
 
@@ -54,6 +54,7 @@ module FriendlyId
54
54
  autoload :Reserved, "friendly_id/reserved"
55
55
  autoload :Scoped, "friendly_id/scoped"
56
56
  autoload :Slugged, "friendly_id/slugged"
57
+ autoload :Globalize, "friendly_id/globalize"
57
58
 
58
59
  # FriendlyId takes advantage of `extended` to do basic model setup, primarily
59
60
  # extending {FriendlyId::Base} to add {FriendlyId::Base#friendly_id
@@ -9,7 +9,7 @@ method to configure your desired options:
9
9
 
10
10
  class Foo < ActiveRecord::Base
11
11
  extend FriendlyId
12
- friendly_id bar, :use => [:slugged, :simple_i18n]
12
+ friendly_id :bar, :use => [:slugged, :simple_i18n]
13
13
  end
14
14
 
15
15
  The most important option is `:use`, which you use to tell FriendlyId which
@@ -0,0 +1,102 @@
1
+ require 'i18n'
2
+
3
+ module FriendlyId
4
+
5
+ =begin
6
+
7
+ == Translate slug db column using Globalize
8
+
9
+ The {FriendlyId::Globalize Globalize} module allow to use
10
+ Globalize (https://github.com/svenfuchs/globalize3) to translate slugs.
11
+
12
+ In order to use this module, your model must have a slug column and set the
13
+ field +slug+ translable with Globalize:
14
+
15
+ class Post < ActiveRecord::Base
16
+ translates :title, :slug
17
+ extend FriendlyId
18
+ friendly_id :title, :use => :globalize
19
+ end
20
+
21
+ === Finds
22
+
23
+ Finds will take into consideration the current locale:
24
+
25
+ I18n.locale = :it
26
+ Post.find("guerre-stellari")
27
+ I18n.locale = :en
28
+ Post.find("star-wars")
29
+
30
+ To find a slug by an explicit locale, perform the find inside a block
31
+ passed to I18n's +with_locale+ method:
32
+
33
+ I18n.with_locale(:it) do
34
+ Post.find("guerre-stellari")
35
+ end
36
+
37
+ === Creating Records
38
+
39
+ When new records are created, the slug is generated for the current locale only.
40
+
41
+ === Translating Slugs
42
+
43
+ To translate an existing record's friendly_id, simply change locale and assign
44
+ +slug+ field:
45
+
46
+ I18n.with_locale(:it) do
47
+ post.slug = "guerre-stellari"
48
+ end
49
+
50
+ =end
51
+ module Globalize
52
+
53
+ def self.included(model_class)
54
+ model_class.instance_eval do
55
+ friendly_id_config.use :slugged
56
+ relation_class.send :include, FinderMethods
57
+ include Model
58
+ # Check if slug field is enabled to be translated with Globalize
59
+ if table_exists?
60
+ if columns.map(&:name).exclude?(friendly_id_config.query_field)
61
+ puts "\n[FriendlyId] Missing field '#{friendly_id_config.query_field}' in DB table '#{table_name}'. This is required for FriendlyId to properly function with the :globalize option.\n\n"
62
+ end
63
+ unless respond_to?('translated_attribute_names') || translated_attribute_names.exclude?(friendly_id_config.query_field.to_sym)
64
+ puts "\n[FriendlyId] You need to translate '#{friendly_id_config.query_field}' field with Globalize (add 'translates :#{friendly_id_config.query_field}' in your model '#{self.class.name}')\n\n"
65
+ end
66
+ end
67
+ end
68
+ end
69
+
70
+ module Model
71
+ def slug=(text)
72
+ set_slug(normalize_friendly_id(text))
73
+ end
74
+ end
75
+
76
+ module FinderMethods
77
+ # FriendlyId overrides this method to make it possible to use friendly id's
78
+ # identically to numeric ids in finders.
79
+ #
80
+ # @example
81
+ # person = Person.find(123)
82
+ # person = Person.find("joe")
83
+ #
84
+ # @see FriendlyId::ObjectUtils
85
+ def find_one(id)
86
+ return super if id.unfriendly_id?
87
+ where(@klass.friendly_id_config.query_field => id).first or
88
+ joins(:translations).
89
+ where(translation_class.arel_table[:locale].eq(I18n.locale)).
90
+ where(translation_class.arel_table[@klass.friendly_id_config.query_field].eq(id)).first or
91
+ # if locale is not translated fallback to default locale
92
+ joins(:translations).
93
+ where(translation_class.arel_table[:locale].eq(I18n.default_locale)).
94
+ where(translation_class.arel_table[@klass.friendly_id_config.query_field].eq(id)).first or
95
+ super
96
+ end
97
+
98
+ protected :find_one
99
+
100
+ end
101
+ end
102
+ end
@@ -65,20 +65,21 @@ method.
65
65
  @friendly_id_config.use :slugged
66
66
  has_many :slugs, :as => :sluggable, :dependent => :destroy,
67
67
  :class_name => Slug.to_s, :order => "#{Slug.quoted_table_name}.id DESC"
68
- before_save :build_slug
68
+ after_save :create_slug
69
69
  relation_class.send :include, FinderMethods
70
+ friendly_id_config.slug_generator_class.send :include, SlugGenerator
70
71
  end
71
72
  end
72
73
 
73
74
  private
74
75
 
75
- def build_slug
76
- return unless should_generate_new_friendly_id?
76
+ def create_slug
77
+ return if slugs.first.try(:slug) == friendly_id
77
78
  # Allow reversion back to a previously used slug
78
79
  relation = slugs.where(:slug => friendly_id)
79
80
  result = relation.select("id").lock(true).all
80
81
  relation.delete_all unless result.empty?
81
- slugs.build :slug => friendly_id
82
+ slugs.create! :slug => friendly_id
82
83
  end
83
84
 
84
85
  # Adds a finder that explictly uses slugs from the slug table.
@@ -110,5 +111,24 @@ method.
110
111
  yield sluggable_id if sluggable_id
111
112
  end
112
113
  end
114
+
115
+ # This module overrides {FriendlyId::SlugGenerator#conflicts} to consider
116
+ # all historic slugs for that model.
117
+ module SlugGenerator
118
+
119
+ private
120
+
121
+ def conflicts
122
+ sluggable_class = friendly_id_config.model_class
123
+ pkey = sluggable_class.primary_key
124
+ value = sluggable.send pkey
125
+ # TODO this is a bit of a performance drain right now; optimize before next release.
126
+ scope = sluggable_class.unscoped.includes(:slugs).where("#{Slug.quoted_table_name}.slug = ? OR #{Slug.quoted_table_name}.slug LIKE ?", normalized, wildcard)
127
+ scope = scope.where(Slug.table_name => {:sluggable_type => sluggable_class.name})
128
+ scope = scope.where("#{sluggable_class.table_name}.#{pkey} <> ?", value) unless sluggable.new_record?
129
+ scope.order("LENGTH(#{Slug.quoted_table_name}.slug) DESC, #{Slug.quoted_table_name}.slug DESC")
130
+ end
131
+
132
+ end
113
133
  end
114
134
  end