mil_friendly_id 4.0.9.8
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.
- 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
|