mil_friendly_id 4.0.9.8

Sign up to get free protection for your applications and to get access to all the features.
Files changed (55) hide show
  1. checksums.yaml +7 -0
  2. data/.gemtest +0 -0
  3. data/.gitignore +12 -0
  4. data/.travis.yml +20 -0
  5. data/.yardopts +4 -0
  6. data/Changelog.md +86 -0
  7. data/Gemfile +15 -0
  8. data/Guide.rdoc +553 -0
  9. data/MIT-LICENSE +19 -0
  10. data/README.md +150 -0
  11. data/Rakefile +108 -0
  12. data/WhatsNew.md +95 -0
  13. data/bench.rb +63 -0
  14. data/friendly_id.gemspec +43 -0
  15. data/gemfiles/Gemfile.rails-3.0.rb +21 -0
  16. data/gemfiles/Gemfile.rails-3.1.rb +22 -0
  17. data/gemfiles/Gemfile.rails-3.2.rb +22 -0
  18. data/geothird_friendly_id.gemspec +45 -0
  19. data/lib/friendly_id.rb +114 -0
  20. data/lib/friendly_id/base.rb +291 -0
  21. data/lib/friendly_id/configuration.rb +80 -0
  22. data/lib/friendly_id/finder_methods.rb +35 -0
  23. data/lib/friendly_id/globalize.rb +115 -0
  24. data/lib/friendly_id/history.rb +134 -0
  25. data/lib/friendly_id/migration.rb +19 -0
  26. data/lib/friendly_id/object_utils.rb +50 -0
  27. data/lib/friendly_id/reserved.rb +68 -0
  28. data/lib/friendly_id/scoped.rb +149 -0
  29. data/lib/friendly_id/simple_i18n.rb +95 -0
  30. data/lib/friendly_id/slug.rb +14 -0
  31. data/lib/friendly_id/slug_generator.rb +80 -0
  32. data/lib/friendly_id/slugged.rb +329 -0
  33. data/lib/generators/friendly_id_generator.rb +17 -0
  34. data/mil_friendly_id.gemspec +45 -0
  35. data/test/base_test.rb +72 -0
  36. data/test/compatibility/ancestry/Gemfile +8 -0
  37. data/test/compatibility/ancestry/ancestry_test.rb +34 -0
  38. data/test/compatibility/threading/Gemfile +8 -0
  39. data/test/compatibility/threading/threading.rb +45 -0
  40. data/test/configuration_test.rb +48 -0
  41. data/test/core_test.rb +48 -0
  42. data/test/databases.yml +19 -0
  43. data/test/generator_test.rb +20 -0
  44. data/test/globalize_test.rb +57 -0
  45. data/test/helper.rb +87 -0
  46. data/test/history_test.rb +149 -0
  47. data/test/object_utils_test.rb +28 -0
  48. data/test/reserved_test.rb +40 -0
  49. data/test/schema.rb +79 -0
  50. data/test/scoped_test.rb +83 -0
  51. data/test/shared.rb +156 -0
  52. data/test/simple_i18n_test.rb +133 -0
  53. data/test/slugged_test.rb +280 -0
  54. data/test/sti_test.rb +77 -0
  55. 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
@@ -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