mil_friendly_id 4.0.9.8
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +7 -0
- data/.gemtest +0 -0
- data/.gitignore +12 -0
- data/.travis.yml +20 -0
- data/.yardopts +4 -0
- data/Changelog.md +86 -0
- data/Gemfile +15 -0
- data/Guide.rdoc +553 -0
- data/MIT-LICENSE +19 -0
- data/README.md +150 -0
- data/Rakefile +108 -0
- data/WhatsNew.md +95 -0
- data/bench.rb +63 -0
- data/friendly_id.gemspec +43 -0
- data/gemfiles/Gemfile.rails-3.0.rb +21 -0
- data/gemfiles/Gemfile.rails-3.1.rb +22 -0
- data/gemfiles/Gemfile.rails-3.2.rb +22 -0
- data/geothird_friendly_id.gemspec +45 -0
- data/lib/friendly_id.rb +114 -0
- data/lib/friendly_id/base.rb +291 -0
- data/lib/friendly_id/configuration.rb +80 -0
- data/lib/friendly_id/finder_methods.rb +35 -0
- data/lib/friendly_id/globalize.rb +115 -0
- data/lib/friendly_id/history.rb +134 -0
- data/lib/friendly_id/migration.rb +19 -0
- data/lib/friendly_id/object_utils.rb +50 -0
- data/lib/friendly_id/reserved.rb +68 -0
- data/lib/friendly_id/scoped.rb +149 -0
- data/lib/friendly_id/simple_i18n.rb +95 -0
- data/lib/friendly_id/slug.rb +14 -0
- data/lib/friendly_id/slug_generator.rb +80 -0
- data/lib/friendly_id/slugged.rb +329 -0
- data/lib/generators/friendly_id_generator.rb +17 -0
- data/mil_friendly_id.gemspec +45 -0
- data/test/base_test.rb +72 -0
- data/test/compatibility/ancestry/Gemfile +8 -0
- data/test/compatibility/ancestry/ancestry_test.rb +34 -0
- data/test/compatibility/threading/Gemfile +8 -0
- data/test/compatibility/threading/threading.rb +45 -0
- data/test/configuration_test.rb +48 -0
- data/test/core_test.rb +48 -0
- data/test/databases.yml +19 -0
- data/test/generator_test.rb +20 -0
- data/test/globalize_test.rb +57 -0
- data/test/helper.rb +87 -0
- data/test/history_test.rb +149 -0
- data/test/object_utils_test.rb +28 -0
- data/test/reserved_test.rb +40 -0
- data/test/schema.rb +79 -0
- data/test/scoped_test.rb +83 -0
- data/test/shared.rb +156 -0
- data/test/simple_i18n_test.rb +133 -0
- data/test/slugged_test.rb +280 -0
- data/test/sti_test.rb +77 -0
- metadata +262 -0
@@ -0,0 +1,21 @@
|
|
1
|
+
source :rubygems
|
2
|
+
|
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
|
+
|
10
|
+
platforms :ruby do
|
11
|
+
gem 'sqlite3'
|
12
|
+
gem 'mysql2', '~> 0.2.0'
|
13
|
+
gem 'pg'
|
14
|
+
end
|
15
|
+
|
16
|
+
gem 'activerecord', '~> 3.0.0'
|
17
|
+
gem 'railties', '~> 3.0.0'
|
18
|
+
gem 'minitest', '3.2.0'
|
19
|
+
gem 'mocha'
|
20
|
+
gem 'rake'
|
21
|
+
gem 'globalize3'
|
@@ -0,0 +1,22 @@
|
|
1
|
+
source :rubygems
|
2
|
+
|
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
|
+
|
10
|
+
platforms :ruby do
|
11
|
+
gem 'sqlite3'
|
12
|
+
gem 'mysql2'
|
13
|
+
gem 'pg'
|
14
|
+
end
|
15
|
+
|
16
|
+
gem 'activerecord', '~> 3.1.0'
|
17
|
+
gem 'railties', '~> 3.1.3'
|
18
|
+
gem 'ffaker'
|
19
|
+
gem 'minitest', '3.2.0'
|
20
|
+
gem 'mocha'
|
21
|
+
gem 'rake'
|
22
|
+
gem 'globalize3'
|
@@ -0,0 +1,22 @@
|
|
1
|
+
source :rubygems
|
2
|
+
|
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
|
+
|
10
|
+
platforms :ruby do
|
11
|
+
gem 'sqlite3'
|
12
|
+
gem 'mysql2'
|
13
|
+
gem 'pg'
|
14
|
+
end
|
15
|
+
|
16
|
+
gem 'ffaker'
|
17
|
+
gem 'activerecord', '~> 3.2.0'
|
18
|
+
gem 'railties', '~> 3.2.0'
|
19
|
+
gem 'minitest', '3.2.0'
|
20
|
+
gem 'mocha'
|
21
|
+
gem 'rake'
|
22
|
+
gem 'globalize3'
|
@@ -0,0 +1,45 @@
|
|
1
|
+
# encoding: utf-8
|
2
|
+
$:.push File.expand_path("../lib", __FILE__)
|
3
|
+
|
4
|
+
require "friendly_id"
|
5
|
+
|
6
|
+
Gem::Specification.new do |s|
|
7
|
+
s.name = "geothird_friendly_id"
|
8
|
+
s.version = FriendlyId::VERSION
|
9
|
+
s.authors = ["Norman Clarke", "Philip Arndt"]
|
10
|
+
s.email = ["norman@njclarke.com", "parndt@gmail.com"]
|
11
|
+
s.homepage = "http://github.com/norman/friendly_id"
|
12
|
+
s.summary = "A comprehensive slugging and pretty-URL plugin."
|
13
|
+
s.rubyforge_project = "friendly_id"
|
14
|
+
s.files = `git ls-files`.split("\n")
|
15
|
+
s.test_files = `git ls-files -- {test}/*`.split("\n")
|
16
|
+
s.require_paths = ["lib"]
|
17
|
+
|
18
|
+
s.add_dependency 'acts_as_paranoid', '0.4.1'
|
19
|
+
|
20
|
+
s.add_development_dependency "railties", "~> 3.2.0"
|
21
|
+
s.add_development_dependency "activerecord", "~> 3.2.0"
|
22
|
+
s.add_development_dependency "minitest", "3.2.0"
|
23
|
+
s.add_development_dependency "mocha"
|
24
|
+
s.add_development_dependency "maruku"
|
25
|
+
s.add_development_dependency "yard"
|
26
|
+
s.add_development_dependency "i18n"
|
27
|
+
s.add_development_dependency "ffaker"
|
28
|
+
s.add_development_dependency "simplecov"
|
29
|
+
s.add_development_dependency "globalize3"
|
30
|
+
|
31
|
+
s.description = <<-EOM
|
32
|
+
FriendlyId is the "Swiss Army bulldozer" of slugging and permalink plugins for
|
33
|
+
Ruby on Rails. It allows you to create pretty URLs and work with human-friendly
|
34
|
+
strings as if they were numeric ids for Active Record models.
|
35
|
+
EOM
|
36
|
+
|
37
|
+
s.post_install_message = <<-EOM
|
38
|
+
NOTE: FriendlyId 4.x breaks compatibility with 3.x. If you're upgrading
|
39
|
+
from 3.x, please see this document:
|
40
|
+
|
41
|
+
http://rubydoc.info/github/norman/friendly_id/master/file/WhatsNew.md
|
42
|
+
|
43
|
+
EOM
|
44
|
+
|
45
|
+
end
|
data/lib/friendly_id.rb
ADDED
@@ -0,0 +1,114 @@
|
|
1
|
+
# encoding: utf-8
|
2
|
+
require "thread"
|
3
|
+
require "friendly_id/base"
|
4
|
+
require "friendly_id/object_utils"
|
5
|
+
require "friendly_id/configuration"
|
6
|
+
require "friendly_id/finder_methods"
|
7
|
+
|
8
|
+
=begin
|
9
|
+
|
10
|
+
== About FriendlyId
|
11
|
+
|
12
|
+
FriendlyId is an add-on to Ruby's Active Record that allows you to replace ids
|
13
|
+
in your URLs with strings:
|
14
|
+
|
15
|
+
# without FriendlyId
|
16
|
+
http://example.com/states/4323454
|
17
|
+
|
18
|
+
# with FriendlyId
|
19
|
+
http://example.com/states/washington
|
20
|
+
|
21
|
+
It requires few changes to your application code and offers flexibility,
|
22
|
+
performance and a well-documented codebase.
|
23
|
+
|
24
|
+
=== Core Concepts
|
25
|
+
|
26
|
+
==== Slugs
|
27
|
+
|
28
|
+
The concept of "slugs[http://en.wikipedia.org/wiki/Slug_(web_publishing)]" is at
|
29
|
+
the heart of FriendlyId.
|
30
|
+
|
31
|
+
A slug is the part of a URL which identifies a page using human-readable
|
32
|
+
keywords, rather than an opaque identifier such as a numeric id. This can make
|
33
|
+
your application more friendly both for users and search engine.
|
34
|
+
|
35
|
+
==== Finders: Slugs Act Like Numeric IDs
|
36
|
+
|
37
|
+
To the extent possible, FriendlyId lets you treat text-based identifiers like
|
38
|
+
normal IDs. This means that you can perform finds with slugs just like you do
|
39
|
+
with numeric ids:
|
40
|
+
|
41
|
+
Person.find(82542335)
|
42
|
+
Person.find("joe")
|
43
|
+
|
44
|
+
=end
|
45
|
+
module FriendlyId
|
46
|
+
|
47
|
+
# The current version.
|
48
|
+
VERSION = "4.0.9.8"
|
49
|
+
|
50
|
+
@mutex = Mutex.new
|
51
|
+
|
52
|
+
autoload :History, "friendly_id/history"
|
53
|
+
autoload :Slug, "friendly_id/slug"
|
54
|
+
autoload :SimpleI18n, "friendly_id/simple_i18n"
|
55
|
+
autoload :Reserved, "friendly_id/reserved"
|
56
|
+
autoload :Scoped, "friendly_id/scoped"
|
57
|
+
autoload :Slugged, "friendly_id/slugged"
|
58
|
+
autoload :Globalize, "friendly_id/globalize"
|
59
|
+
|
60
|
+
# FriendlyId takes advantage of `extended` to do basic model setup, primarily
|
61
|
+
# extending {FriendlyId::Base} to add {FriendlyId::Base#friendly_id
|
62
|
+
# friendly_id} as a class method.
|
63
|
+
#
|
64
|
+
# Previous versions of FriendlyId simply patched ActiveRecord::Base, but this
|
65
|
+
# version tries to be less invasive.
|
66
|
+
#
|
67
|
+
# In addition to adding {FriendlyId::Base#friendly_id friendly_id}, the class
|
68
|
+
# instance variable +@friendly_id_config+ is added. This variable is an
|
69
|
+
# instance of an anonymous subclass of {FriendlyId::Configuration}. This
|
70
|
+
# allows subsequently loaded modules like {FriendlyId::Slugged} and
|
71
|
+
# {FriendlyId::Scoped} to add functionality to the configuration class only
|
72
|
+
# for the current class, rather than monkey patching
|
73
|
+
# {FriendlyId::Configuration} directly. This isolates other models from large
|
74
|
+
# feature changes an addon to FriendlyId could potentially introduce.
|
75
|
+
#
|
76
|
+
# The upshot of this is, you can have two Active Record models that both have
|
77
|
+
# a @friendly_id_config, but each config object can have different methods
|
78
|
+
# and behaviors depending on what modules have been loaded, without
|
79
|
+
# conflicts. Keep this in mind if you're hacking on FriendlyId.
|
80
|
+
#
|
81
|
+
# For examples of this, see the source for {Scoped.included}.
|
82
|
+
def self.extended(model_class)
|
83
|
+
return if model_class.respond_to? :friendly_id
|
84
|
+
class << model_class
|
85
|
+
alias relation_without_friendly_id relation
|
86
|
+
end
|
87
|
+
model_class.instance_eval do
|
88
|
+
extend Base
|
89
|
+
@friendly_id_config = Class.new(Configuration).new(self)
|
90
|
+
FriendlyId.defaults.call @friendly_id_config
|
91
|
+
end
|
92
|
+
end
|
93
|
+
|
94
|
+
# Allow developers to `include` FriendlyId or `extend` it.
|
95
|
+
def self.included(model_class)
|
96
|
+
model_class.extend self
|
97
|
+
end
|
98
|
+
|
99
|
+
# Set global defaults for all models using FriendlyId.
|
100
|
+
#
|
101
|
+
# The default defaults are to use the +:reserved+ module and nothing else.
|
102
|
+
#
|
103
|
+
# @example
|
104
|
+
# FriendlyId.defaults do |config|
|
105
|
+
# config.base = :name
|
106
|
+
# config.use :slugged
|
107
|
+
# end
|
108
|
+
def self.defaults(&block)
|
109
|
+
@mutex.synchronize do
|
110
|
+
@defaults = block if block_given?
|
111
|
+
@defaults ||= lambda {|config| config.use :reserved}
|
112
|
+
end
|
113
|
+
end
|
114
|
+
end
|
@@ -0,0 +1,291 @@
|
|
1
|
+
module FriendlyId
|
2
|
+
=begin
|
3
|
+
|
4
|
+
== Setting Up FriendlyId in Your Model
|
5
|
+
|
6
|
+
To use FriendlyId in your ActiveRecord models, you must first either extend or
|
7
|
+
include the FriendlyId module (it makes no difference), then invoke the
|
8
|
+
{FriendlyId::Base#friendly_id friendly_id} method to configure your desired
|
9
|
+
options:
|
10
|
+
|
11
|
+
class Foo < ActiveRecord::Base
|
12
|
+
include FriendlyId
|
13
|
+
friendly_id :bar, :use => [:slugged, :simple_i18n]
|
14
|
+
end
|
15
|
+
|
16
|
+
The most important option is `:use`, which you use to tell FriendlyId which
|
17
|
+
addons it should use. See the documentation for this method for a list of all
|
18
|
+
available addons, or skim through the rest of the docs to get a high-level
|
19
|
+
overview.
|
20
|
+
|
21
|
+
=== The Default Setup: Simple Models
|
22
|
+
|
23
|
+
The simplest way to use FriendlyId is with a model that has a uniquely indexed
|
24
|
+
column with no spaces or special characters, and that is seldom or never
|
25
|
+
updated. The most common example of this is a user name:
|
26
|
+
|
27
|
+
class User < ActiveRecord::Base
|
28
|
+
extend FriendlyId
|
29
|
+
friendly_id :login
|
30
|
+
validates_format_of :login, :with => /\A[a-z0-9]+\z/i
|
31
|
+
end
|
32
|
+
|
33
|
+
@user = User.find "joe" # the old User.find(1) still works, too
|
34
|
+
@user.to_param # returns "joe"
|
35
|
+
redirect_to @user # the URL will be /users/joe
|
36
|
+
|
37
|
+
In this case, FriendlyId assumes you want to use the column as-is; it will never
|
38
|
+
modify the value of the column, and your application should ensure that the
|
39
|
+
value is unique and admissible in a URL:
|
40
|
+
|
41
|
+
class City < ActiveRecord::Base
|
42
|
+
extend FriendlyId
|
43
|
+
friendly_id :name
|
44
|
+
end
|
45
|
+
|
46
|
+
@city.find "Viña del Mar"
|
47
|
+
redirect_to @city # the URL will be /cities/Viña%20del%20Mar
|
48
|
+
|
49
|
+
Writing the code to process an arbitrary string into a good identifier for use
|
50
|
+
in a URL can be repetitive and surprisingly tricky, so for this reason it's
|
51
|
+
often better and easier to use {FriendlyId::Slugged slugs}.
|
52
|
+
|
53
|
+
=end
|
54
|
+
module Base
|
55
|
+
|
56
|
+
# Configure FriendlyId's behavior in a model.
|
57
|
+
#
|
58
|
+
# class Post < ActiveRecord::Base
|
59
|
+
# extend FriendlyId
|
60
|
+
# friendly_id :title, :use => :slugged
|
61
|
+
# end
|
62
|
+
#
|
63
|
+
# When given the optional block, this method will yield the class's instance
|
64
|
+
# of {FriendlyId::Configuration} to the block before evaluating other
|
65
|
+
# arguments, so configuration values set in the block may be overwritten by
|
66
|
+
# the arguments. This order was chosen to allow passing the same proc to
|
67
|
+
# multiple models, while being able to override the values it sets. Here is
|
68
|
+
# a contrived example:
|
69
|
+
#
|
70
|
+
# $friendly_id_config_proc = Proc.new do |config|
|
71
|
+
# config.base = :name
|
72
|
+
# config.use :slugged
|
73
|
+
# end
|
74
|
+
#
|
75
|
+
# class Foo < ActiveRecord::Base
|
76
|
+
# extend FriendlyId
|
77
|
+
# friendly_id &$friendly_id_config_proc
|
78
|
+
# end
|
79
|
+
#
|
80
|
+
# class Bar < ActiveRecord::Base
|
81
|
+
# extend FriendlyId
|
82
|
+
# friendly_id :title, &$friendly_id_config_proc
|
83
|
+
# end
|
84
|
+
#
|
85
|
+
# However, it's usually better to use {FriendlyId.defaults} for this:
|
86
|
+
#
|
87
|
+
# FriendlyId.defaults do |config|
|
88
|
+
# config.base = :name
|
89
|
+
# config.use :slugged
|
90
|
+
# end
|
91
|
+
#
|
92
|
+
# class Foo < ActiveRecord::Base
|
93
|
+
# extend FriendlyId
|
94
|
+
# end
|
95
|
+
#
|
96
|
+
# class Bar < ActiveRecord::Base
|
97
|
+
# extend FriendlyId
|
98
|
+
# friendly_id :title
|
99
|
+
# end
|
100
|
+
#
|
101
|
+
# In general you should use the block syntax either because of your personal
|
102
|
+
# aesthetic preference, or because you need to share some functionality
|
103
|
+
# between multiple models that can't be well encapsulated by
|
104
|
+
# {FriendlyId.defaults}.
|
105
|
+
#
|
106
|
+
# === Order Method Calls in a Block vs Ordering Options
|
107
|
+
#
|
108
|
+
# When calling this method without a block, you may set the hash options in
|
109
|
+
# any order.
|
110
|
+
#
|
111
|
+
# However, when using block-style invocation, be sure to call
|
112
|
+
# FriendlyId::Configuration's {FriendlyId::Configuration#use use} method
|
113
|
+
# *prior* to the associated configuration options, because it will include
|
114
|
+
# modules into your class, and these modules in turn may add required
|
115
|
+
# configuration options to the +@friendly_id_configuraton+'s class:
|
116
|
+
#
|
117
|
+
# class Person < ActiveRecord::Base
|
118
|
+
# friendly_id do |config|
|
119
|
+
# # This will work
|
120
|
+
# config.use :slugged
|
121
|
+
# config.sequence_separator = ":"
|
122
|
+
# end
|
123
|
+
# end
|
124
|
+
#
|
125
|
+
# class Person < ActiveRecord::Base
|
126
|
+
# friendly_id do |config|
|
127
|
+
# # This will fail
|
128
|
+
# config.sequence_separator = ":"
|
129
|
+
# config.use :slugged
|
130
|
+
# end
|
131
|
+
# end
|
132
|
+
#
|
133
|
+
# === Including Your Own Modules
|
134
|
+
#
|
135
|
+
# Because :use can accept a name or a Module, {FriendlyId.defaults defaults}
|
136
|
+
# can be a convenient place to set up behavior common to all classes using
|
137
|
+
# FriendlyId. You can include any module, or more conveniently, define one
|
138
|
+
# on-the-fly. For example, let's say you want to make
|
139
|
+
# Babosa[http://github.com/norman/babosa] the default slugging library in
|
140
|
+
# place of Active Support, and transliterate all slugs from Russian Cyrillic
|
141
|
+
# to ASCII:
|
142
|
+
#
|
143
|
+
# require "babosa"
|
144
|
+
#
|
145
|
+
# FriendlyId.defaults do |config|
|
146
|
+
# config.base = :name
|
147
|
+
# config.use :slugged
|
148
|
+
# config.use Module.new {
|
149
|
+
# def normalize_friendly_id(text)
|
150
|
+
# text.to_slug.normalize(:transliterations => [:russian, :latin])
|
151
|
+
# end
|
152
|
+
# }
|
153
|
+
# end
|
154
|
+
#
|
155
|
+
#
|
156
|
+
# @option options [Symbol,Module] :use The addon or name of an addon to use.
|
157
|
+
# By default, FriendlyId provides {FriendlyId::Slugged :slugged},
|
158
|
+
# {FriendlyId::History :history}, {FriendlyId::Reserved :reserved}, and
|
159
|
+
# {FriendlyId::Scoped :scoped}, {FriendlyId::SimpleI18n :simple_i18n},
|
160
|
+
# and {FriendlyId::Globalize :globalize}.
|
161
|
+
#
|
162
|
+
# @option options [Array] :reserved_words Available when using +:reserved+,
|
163
|
+
# which is loaded by default. Sets an array of words banned for use as
|
164
|
+
# the basis of a friendly_id. By default this includes "edit" and "new".
|
165
|
+
#
|
166
|
+
# @option options [Symbol] :scope Available when using +:scoped+.
|
167
|
+
# Sets the relation or column used to scope generated friendly ids. This
|
168
|
+
# option has no default value.
|
169
|
+
#
|
170
|
+
# @option options [Symbol] :sequence_separator Available when using +:slugged+.
|
171
|
+
# Configures the sequence of characters used to separate a slug from a
|
172
|
+
# sequence. Defaults to +--+.
|
173
|
+
#
|
174
|
+
# @option options [Symbol] :slug_column Available when using +:slugged+.
|
175
|
+
# Configures the name of the column where FriendlyId will store the slug.
|
176
|
+
# Defaults to +:slug+.
|
177
|
+
#
|
178
|
+
# @option options [Symbol] :slug_generator_class Available when using +:slugged+.
|
179
|
+
# Sets the class used to generate unique slugs. You should not specify this
|
180
|
+
# unless you're doing some extensive hacking on FriendlyId. Defaults to
|
181
|
+
# {FriendlyId::SlugGenerator}.
|
182
|
+
#
|
183
|
+
# @yield Provides access to the model class's friendly_id_config, which
|
184
|
+
# allows an alternate configuration syntax, and conditional configuration
|
185
|
+
# logic.
|
186
|
+
#
|
187
|
+
# @yieldparam config The model class's {FriendlyId::Configuration friendly_id_config}.
|
188
|
+
def friendly_id(base = nil, options = {}, &block)
|
189
|
+
yield friendly_id_config if block_given?
|
190
|
+
friendly_id_config.use options.delete :use
|
191
|
+
friendly_id_config.send :set, base ? options.merge(:base => base) : options
|
192
|
+
before_save {|rec| rec.instance_eval {@current_friendly_id = friendly_id}}
|
193
|
+
include Model
|
194
|
+
end
|
195
|
+
|
196
|
+
# Returns the model class's {FriendlyId::Configuration friendly_id_config}.
|
197
|
+
# @note In the case of Single Table Inheritance (STI), this method will
|
198
|
+
# duplicate the parent class's FriendlyId::Configuration and relation class
|
199
|
+
# on first access. If you're concerned about thread safety, then be sure
|
200
|
+
# to invoke {#friendly_id} in your class for each model.
|
201
|
+
def friendly_id_config
|
202
|
+
@friendly_id_config ||= base_class.friendly_id_config.dup.tap do |config|
|
203
|
+
config.model_class = self
|
204
|
+
@relation_class = base_class.send(:relation_class)
|
205
|
+
end
|
206
|
+
end
|
207
|
+
|
208
|
+
private
|
209
|
+
|
210
|
+
# Gets an instance of an the relation class.
|
211
|
+
#
|
212
|
+
# With FriendlyId this will be a subclass of ActiveRecord::Relation, rather than
|
213
|
+
# Relation itself, in order to avoid tainting all Active Record models with
|
214
|
+
# FriendlyId.
|
215
|
+
#
|
216
|
+
# Note that this method is essentially copied and pasted from Rails 3.2.9.rc1,
|
217
|
+
# with the exception of changing the relation class. Obviously this is less than
|
218
|
+
# ideal, but I know of no better way to accomplish this.
|
219
|
+
# @see #relation_class
|
220
|
+
def relation #:nodoc:
|
221
|
+
relation = relation_class.new(self, arel_table)
|
222
|
+
|
223
|
+
if finder_needs_type_condition?
|
224
|
+
relation.with_deleted.where(type_condition).create_with(inheritance_column.to_sym => sti_name)
|
225
|
+
else
|
226
|
+
relation
|
227
|
+
end
|
228
|
+
end
|
229
|
+
|
230
|
+
# Gets (and if necessary, creates) a subclass of the model's relation class.
|
231
|
+
#
|
232
|
+
# Rather than including FriendlyId's overridden finder methods in
|
233
|
+
# ActiveRecord::Relation directly, FriendlyId adds them to a subclass
|
234
|
+
# specific to the AR model, and makes #relation return an instance of this
|
235
|
+
# class. By doing this, we ensure that only models that specifically extend
|
236
|
+
# FriendlyId have their finder methods overridden.
|
237
|
+
#
|
238
|
+
# Note that this method does not directly subclass ActiveRecord::Relation,
|
239
|
+
# but rather whatever class the @relation class instance variable is an
|
240
|
+
# instance of. In practice, this will almost always end up being
|
241
|
+
# ActiveRecord::Relation, but in case another plugin is using this same
|
242
|
+
# pattern to extend a model's finder functionality, FriendlyId will not
|
243
|
+
# replace it, but rather override it.
|
244
|
+
#
|
245
|
+
# This pattern can be seen as a poor man's "refinement"
|
246
|
+
# (http://timelessrepo.com/refinements-in-ruby), and while I **think** it
|
247
|
+
# will work quite well, I realize that it could cause unexpected issues,
|
248
|
+
# since the authors of Rails are probably not intending this kind of usage
|
249
|
+
# against a private API. If this ends up being problematic I will probably
|
250
|
+
# revert back to the old behavior of simply extending
|
251
|
+
# ActiveRecord::Relation.
|
252
|
+
def relation_class
|
253
|
+
@relation_class or begin
|
254
|
+
@relation_class = Class.new(relation_without_friendly_id.class) do
|
255
|
+
alias_method :find_one_without_friendly_id, :find_one
|
256
|
+
alias_method :exists_without_friendly_id?, :exists?
|
257
|
+
include FriendlyId::FinderMethods
|
258
|
+
end
|
259
|
+
# Set a name so that model instances can be marshalled. Use a
|
260
|
+
# ridiculously long name that will not conflict with anything.
|
261
|
+
# TODO: just use the constant, no need for the @relation_class variable.
|
262
|
+
const_set('FriendlyIdActiveRecordRelation', @relation_class)
|
263
|
+
end
|
264
|
+
end
|
265
|
+
end
|
266
|
+
|
267
|
+
# Instance methods that will be added to all classes using FriendlyId.
|
268
|
+
module Model
|
269
|
+
|
270
|
+
attr_reader :current_friendly_id
|
271
|
+
|
272
|
+
# Convenience method for accessing the class method of the same name.
|
273
|
+
def friendly_id_config
|
274
|
+
self.class.friendly_id_config
|
275
|
+
end
|
276
|
+
|
277
|
+
# Get the instance's friendly_id.
|
278
|
+
def friendly_id
|
279
|
+
send friendly_id_config.query_field
|
280
|
+
end
|
281
|
+
|
282
|
+
# Either the friendly_id, or the numeric id cast to a string.
|
283
|
+
def to_param
|
284
|
+
if diff = changes[friendly_id_config.query_field]
|
285
|
+
diff.first || diff.second
|
286
|
+
else
|
287
|
+
friendly_id.presence || super
|
288
|
+
end
|
289
|
+
end
|
290
|
+
end
|
291
|
+
end
|