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