friendly_id 2.3.4 → 3.0.0.beta1

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 (47) hide show
  1. data/Changelog.md +10 -0
  2. data/Contributors.md +1 -0
  3. data/Guide.md +48 -4
  4. data/README.md +6 -4
  5. data/Rakefile +1 -1
  6. data/extras/extras.rb +1 -1
  7. data/generators/friendly_id/friendly_id_generator.rb +1 -1
  8. data/lib/friendly_id.rb +4 -6
  9. data/lib/friendly_id/{active_record2 → acktive_record}/configuration.rb +2 -2
  10. data/lib/friendly_id/{active_record2 → acktive_record}/finders.rb +24 -10
  11. data/lib/friendly_id/{active_record2 → acktive_record}/simple_model.rb +12 -50
  12. data/lib/friendly_id/acktive_record/slug.rb +66 -0
  13. data/lib/friendly_id/{active_record2 → acktive_record}/slugged_model.rb +9 -85
  14. data/lib/friendly_id/{active_record2 → acktive_record}/tasks.rb +5 -2
  15. data/lib/friendly_id/{active_record2 → acktive_record}/tasks/friendly_id.rake +1 -1
  16. data/lib/friendly_id/{active_record2.rb → active_record.rb} +18 -13
  17. data/lib/friendly_id/configuration.rb +11 -26
  18. data/lib/friendly_id/finders.rb +5 -3
  19. data/lib/friendly_id/slug_string.rb +11 -10
  20. data/lib/friendly_id/slugged.rb +11 -4
  21. data/lib/friendly_id/test.rb +46 -5
  22. data/lib/friendly_id/version.rb +5 -4
  23. data/lib/generators/friendly_id_generator.rb +32 -0
  24. data/rails/init.rb +1 -1
  25. data/test/{active_record2 → acktive_record}/basic_slugged_model_test.rb +3 -3
  26. data/test/{active_record2 → acktive_record}/cached_slug_test.rb +3 -3
  27. data/test/{active_record2 → acktive_record}/core.rb +1 -1
  28. data/test/{active_record2 → acktive_record}/custom_normalizer_test.rb +3 -3
  29. data/test/{active_record2 → acktive_record}/custom_table_name_test.rb +3 -3
  30. data/test/{active_record2 → acktive_record}/scoped_model_test.rb +2 -2
  31. data/test/{active_record2 → acktive_record}/simple_test.rb +4 -1
  32. data/test/{active_record2 → acktive_record}/slug_test.rb +0 -0
  33. data/test/{active_record2 → acktive_record}/slugged.rb +1 -1
  34. data/test/{active_record2 → acktive_record}/slugged_status_test.rb +1 -1
  35. data/test/{active_record2 → acktive_record}/sti_test.rb +3 -3
  36. data/test/{active_record2 → acktive_record}/support/database.mysql.yml +0 -0
  37. data/test/{active_record2 → acktive_record}/support/database.postgres.yml +0 -0
  38. data/test/{active_record2 → acktive_record}/support/database.sqlite3.yml +0 -0
  39. data/test/{active_record2 → acktive_record}/support/models.rb +5 -6
  40. data/test/{active_record2 → acktive_record}/tasks_test.rb +1 -1
  41. data/test/acktive_record/temp_test.rb +32 -0
  42. data/test/{active_record2 → acktive_record}/test_helper.rb +4 -10
  43. data/test/slug_string_test.rb +5 -1
  44. data/test/test_helper.rb +9 -3
  45. metadata +48 -44
  46. data/lib/friendly_id/active_record2/slug.rb +0 -111
  47. data/test/active_record2/deprecated_test.rb +0 -23
@@ -1,5 +1,5 @@
1
1
  module FriendlyId
2
- module ActiveRecord2
2
+ module AcktiveRecord
3
3
  module SluggedModel
4
4
 
5
5
  module SluggedFinder
@@ -18,7 +18,7 @@ module FriendlyId
18
18
  class MultipleFinder
19
19
 
20
20
  include FriendlyId::Finders::Base
21
- include FriendlyId::ActiveRecord2::Finders::Multiple
21
+ include FriendlyId::AcktiveRecord::Finders::Multiple
22
22
  include SluggedFinder
23
23
 
24
24
  attr_reader :slugs
@@ -86,7 +86,7 @@ module FriendlyId
86
86
  include SluggedFinder
87
87
 
88
88
  def find
89
- @result = with_scope({:find => find_options}) { find_initial options }
89
+ @result = model_class.scoped(find_options).first(options)
90
90
  handle_friendly_result if friendly?
91
91
  @result
92
92
  rescue ::ActiveRecord::RecordNotFound => @error
@@ -109,7 +109,7 @@ module FriendlyId
109
109
  end
110
110
 
111
111
  def raise_scoped_error
112
- scope_message = options[:scope] || "expected, but none given"
112
+ scope_message = scope || "expected, but none given"
113
113
  message = "%s, scope: %s" % [@error.message, scope_message]
114
114
  raise ::ActiveRecord::RecordNotFound, message
115
115
  end
@@ -124,7 +124,7 @@ module FriendlyId
124
124
  include SluggedFinder
125
125
 
126
126
  def find
127
- @result = with_scope({:find => find_options}) { find_initial options }
127
+ @result = model_class.scoped(find_options).first(options)
128
128
  handle_friendly_result if friendly?
129
129
  @result
130
130
  rescue ActiveRecord::RecordNotFound
@@ -138,81 +138,6 @@ module FriendlyId
138
138
 
139
139
  end
140
140
 
141
- # The methods in this module override ActiveRecord's +find_one+ and
142
- # +find_some+ to add FriendlyId's features.
143
- module FinderMethods
144
-
145
- protected
146
-
147
- def find_one(id_or_name, options)
148
- finder = Finders::FinderProxy.new(id_or_name, self, options)
149
- finder.unfriendly? ? super : finder.find or super
150
- end
151
-
152
- def find_some(ids_and_names, options)
153
- Finders::FinderProxy.new(ids_and_names, self, options).find
154
- end
155
-
156
- # Since Rails goes out of its way to make these options completely
157
- # inaccessible, we have to copy them here.
158
- def validate_find_options(options)
159
- options.assert_valid_keys([:conditions, :include, :joins, :limit, :offset,
160
- :order, :select, :readonly, :group, :from, :lock, :having, :scope])
161
- end
162
-
163
- end
164
-
165
- # These methods will be removed in FriendlyId 3.0.
166
- module DeprecatedMethods
167
-
168
- # @deprecated Please use #to_param
169
- def best_id
170
- warn("best_id is deprecated and will be removed in 3.0. Please use #to_param.")
171
- to_param
172
- end
173
-
174
- # @deprecated Please use #friendly_id_status.slug.
175
- def finder_slug
176
- warn("finder_slug is deprecated and will be removed in 3.0. Please use #friendly_id_status.slug.")
177
- friendly_id_status.slug
178
- end
179
-
180
- # Was the record found using one of its friendly ids?
181
- # @deprecated Please use #friendly_id_status.friendly?
182
- def found_using_friendly_id?
183
- warn("found_using_friendly_id? is deprecated and will be removed in 3.0. Please use #friendly_id_status.friendly?")
184
- friendly_id_status.friendly?
185
- end
186
-
187
- # Was the record found using its numeric id?
188
- # @deprecated Please use #friendly_id_status.numeric?
189
- def found_using_numeric_id?
190
- warn("found_using_numeric_id is deprecated and will be removed in 3.0. Please use #friendly_id_status.numeric?")
191
- friendly_id_status.numeric?
192
- end
193
-
194
- # Was the record found using an old friendly id?
195
- # @deprecated Please use #friendly_id_status.outdated?
196
- def found_using_outdated_friendly_id?
197
- warn("found_using_outdated_friendly_id is deprecated and will be removed in 3.0. Please use #friendly_id_status.outdated?")
198
- friendly_id_status.outdated?
199
- end
200
-
201
- # Was the record found using an old friendly id, or its numeric id?
202
- # @deprecated Please use !#friendly_id_status.best?
203
- def has_better_id?
204
- warn("has_better_id? is deprecated and will be removed in 3.0. Please use !#friendly_id_status.best?")
205
- ! friendly_id_status.best?
206
- end
207
-
208
- # @deprecated Please use #slug?
209
- def has_a_slug?
210
- warn("has_a_slug? is deprecated and will be removed in 3.0. Please use #slug?")
211
- slug?
212
- end
213
-
214
- end
215
-
216
141
  def self.included(base)
217
142
  base.class_eval do
218
143
  has_many :slugs, :order => 'id DESC', :as => :sluggable, :dependent => :destroy
@@ -221,18 +146,17 @@ module FriendlyId
221
146
  after_update :update_scope
222
147
  after_update :update_dependent_scopes
223
148
  protect_friendly_id_attributes
224
- extend FinderMethods
149
+ extend FriendlyId::AcktiveRecord::FinderMethods
225
150
  end
226
151
  end
227
152
 
228
153
  include FriendlyId::Slugged::Model
229
- include DeprecatedMethods
230
154
 
231
155
  def find_slug(name, sequence)
232
156
  slugs.find_by_name_and_sequence(name, sequence)
233
157
  end
234
158
 
235
- # The model instance's current {FriendlyId::ActiveRecord2::Slug slug}.
159
+ # The model instance's current {FriendlyId::AcktiveRecord::Slug slug}.
236
160
  def slug
237
161
  return @slug if new_record?
238
162
  @slug ||= slugs.first(:order => "id DESC")
@@ -273,7 +197,7 @@ module FriendlyId
273
197
 
274
198
  # Reset the cached friendly_id?
275
199
  def new_cache_needed?
276
- uses_slug_cache? && send(friendly_id_config.cache_column) != slug.to_friendly_id
200
+ uses_slug_cache? && slug? && send(friendly_id_config.cache_column) != slug.to_friendly_id
277
201
  end
278
202
 
279
203
  # Reset the cached friendly_id.
@@ -285,7 +209,7 @@ module FriendlyId
285
209
  end
286
210
 
287
211
  def update_scope
288
- return unless scope_changed?
212
+ return unless slug && scope_changed?
289
213
  slug.update_attributes :scope => send(friendly_id_config.scope).to_param
290
214
  rescue ActiveRecord::StatementInvalid
291
215
  slug.update_attributes :sequence => Slug.similar_to(slug).first.sequence.succ
@@ -26,13 +26,15 @@ module FriendlyId
26
26
 
27
27
  def make_slugs
28
28
  validate_uses_slugs
29
- options = {:limit => 100, :include => :slugs, :conditions => "slugs.id IS NULL"}.merge(task_options || {})
29
+ cond = "slugs.id IS NULL"
30
+ options = {:limit => 100, :include => :slugs, :conditions => cond, :order => "#{klass.table_name}.id ASC"}.merge(task_options || {})
30
31
  while records = find(:all, options) do
31
32
  break if records.size == 0
32
33
  records.each do |record|
33
34
  record.save!
34
35
  yield(record) if block_given?
35
36
  end
37
+ options[:conditions] = cond + " and #{klass.table_name}.id > #{records.last.id}"
36
38
  end
37
39
  end
38
40
 
@@ -45,7 +47,7 @@ module FriendlyId
45
47
  end
46
48
 
47
49
  def delete_old_slugs
48
- conditions = ["created_at < ?", DateTime.now - days.days]
50
+ conditions = ["created_at < ?", DateTime.now - days]
49
51
  if klass
50
52
  conditions[0] << " AND sluggable_type = ?"
51
53
  conditions << klass.to_s
@@ -54,6 +56,7 @@ module FriendlyId
54
56
  end
55
57
 
56
58
  def validate_uses_slugs
59
+ (raise "You need to pass a MODEL=<model name> argument to rake") if klass.blank?
57
60
  unless friendly_id_config.use_slug?
58
61
  raise "Class '%s' doesn't use slugs" % klass.to_s
59
62
  end
@@ -2,7 +2,7 @@ namespace :friendly_id do
2
2
  desc "Make slugs for a model."
3
3
  task :make_slugs => :environment do
4
4
  FriendlyId::TaskRunner.new.make_slugs do |record|
5
- puts "%s(%d): friendly_id set to '%s'" % [record.class.to_s, record.id, record.slug.name]
5
+ puts "%s(%d): friendly_id set to '%s'" % [record.class.to_s, record.id, record.slug.name] if record.slug
6
6
  end
7
7
  end
8
8
 
@@ -1,20 +1,18 @@
1
- require File.join(File.dirname(__FILE__), "active_record2", "configuration")
2
- require File.join(File.dirname(__FILE__), "active_record2", "finders")
3
- require File.join(File.dirname(__FILE__), "active_record2", "simple_model")
4
- require File.join(File.dirname(__FILE__), "active_record2", "slugged_model")
5
- require File.join(File.dirname(__FILE__), "active_record2", "slug")
6
- require File.join(File.dirname(__FILE__), "active_record2", "tasks")
7
-
8
1
  module FriendlyId
9
2
 
10
- module ActiveRecord2
3
+ module AcktiveRecord
4
+
5
+ module Compat
6
+ def self.scope_method
7
+ ActiveRecord::VERSION::STRING >= "3" ? :scope : :named_scope
8
+ end
9
+ end
11
10
 
12
11
  include FriendlyId::Base
13
12
 
14
- def has_friendly_id(method, options = {}, &block)
13
+ def has_friendly_id(method, options = {})
15
14
  class_inheritable_accessor :friendly_id_config
16
- write_inheritable_attribute :friendly_id_config, Configuration.new(self,
17
- method, options.merge(:normalizer => block))
15
+ write_inheritable_attribute :friendly_id_config, Configuration.new(self, method, options)
18
16
  if friendly_id_config.use_slug?
19
17
  include SluggedModel
20
18
  else
@@ -43,5 +41,12 @@ module FriendlyId
43
41
  end
44
42
 
45
43
  class ActiveRecord::Base
46
- extend FriendlyId::ActiveRecord2
47
- end
44
+ extend FriendlyId::AcktiveRecord
45
+ end
46
+
47
+ require File.join(File.dirname(__FILE__), "acktive_record", "configuration")
48
+ require File.join(File.dirname(__FILE__), "acktive_record", "finders")
49
+ require File.join(File.dirname(__FILE__), "acktive_record", "simple_model")
50
+ require File.join(File.dirname(__FILE__), "acktive_record", "slugged_model")
51
+ require File.join(File.dirname(__FILE__), "acktive_record", "slug")
52
+ require File.join(File.dirname(__FILE__), "acktive_record", "tasks")
@@ -22,6 +22,7 @@ module FriendlyId
22
22
  class Configuration
23
23
 
24
24
  DEFAULTS = {
25
+ :allow_nil => false,
25
26
  :ascii_approximation_options => [],
26
27
  :max_length => 255,
27
28
  :reserved_words => ["index", "new"],
@@ -29,6 +30,12 @@ module FriendlyId
29
30
  :sequence_separator => "--"
30
31
  }
31
32
 
33
+ # Whether to allow friendly_id and/or slugs to be nil. This is not
34
+ # generally useful on its own, but may allow you greater flexibility to
35
+ # customize your application.
36
+ attr_accessor :allow_nil
37
+ alias :allow_nil? :allow_nil
38
+
32
39
  # Strip diacritics from Western characters.
33
40
  attr_accessor :approximate_ascii
34
41
 
@@ -47,12 +54,6 @@ module FriendlyId
47
54
  attr_reader :method
48
55
  alias :column :method
49
56
 
50
- # A block or proc through which to filter the friendly_id text.
51
- # This method will be removed from FriendlyId 3.0.
52
- # @deprecated Please override the +normalize_friendly_id+
53
- # method in your model class rather than passing a block to `has_friendly_id`.
54
- attr_accessor :normalizer
55
-
56
57
  # The message shown when a reserved word is used.
57
58
  # @see #reserved_words
58
59
  attr_accessor :reserved_message
@@ -85,10 +86,8 @@ module FriendlyId
85
86
  yield self if block_given?
86
87
  end
87
88
 
88
- def normalizer=(arg)
89
- return if arg.nil?
90
- warn("passing a block to has_friendly_id is deprecated and will be removed from 3.0. Please override #normalize_friendly_id.")
91
- @normalizer = arg
89
+ def forbid_nil?
90
+ !allow_nil?
92
91
  end
93
92
 
94
93
  def reserved_words=(*words)
@@ -103,21 +102,7 @@ module FriendlyId
103
102
  [method, reserved_message % word] if reserved? word
104
103
  end
105
104
 
106
- # This method will be removed from FriendlyId 3.0.
107
- # @deprecated Please use {#reserved_words reserved_words}.
108
- def reserved=(*args)
109
- warn('The "reserved" option is deprecated and will be removed from FriendlyId 3.0. Please use "reserved_words".')
110
- self.reserved_words = *args
111
- end
112
-
113
- # This method will be removed from FriendlyId 3.0.
114
- # @deprecated Please use {#approximate_ascii approximate_ascii}.
115
- def strip_diacritics=(*args)
116
- warn('strip_diacritics is deprecated and will be removed from 3.0. Please use #approximate_ascii')
117
- self.approximate_ascii = *args
118
- end
119
-
120
- %w[approximate_ascii normalizer scope strip_non_ascii use_slug].each do |method|
105
+ %w[approximate_ascii scope strip_non_ascii use_slug].each do |method|
121
106
  class_eval(<<-EOM)
122
107
  def #{method}?
123
108
  !! #{method}
@@ -129,4 +114,4 @@ module FriendlyId
129
114
 
130
115
  end
131
116
 
132
- end
117
+ end
@@ -14,7 +14,7 @@ module FriendlyId
14
14
  # @return [true, false, nil]
15
15
  # @see #unfriendly?
16
16
  def self.friendly?(id)
17
- if id.is_a?(Integer) or id.class.respond_to? :friendly_id_config
17
+ if id.is_a?(Integer) or id.is_a?(Symbol) or id.class.respond_to? :friendly_id_config
18
18
  return false
19
19
  elsif id.to_i.to_s != id.to_s
20
20
  return true
@@ -35,7 +35,7 @@ module FriendlyId
35
35
  self.ids = ids
36
36
  self.options = options
37
37
  self.model_class = model_class
38
- self.scope = options[:scope]
38
+ self.scope = options.delete :scope
39
39
  end
40
40
 
41
41
  def method_missing(*args, &block)
@@ -67,7 +67,9 @@ module FriendlyId
67
67
  alias :id= :ids=
68
68
 
69
69
  def scope=(scope)
70
- @scope = scope.to_param unless scope.nil?
70
+ unless scope.nil?
71
+ @scope = scope.respond_to?(:to_param) ? scope.to_param : scope.to_s
72
+ end
71
73
  end
72
74
  end
73
75
 
@@ -162,7 +162,7 @@ module FriendlyId
162
162
  # whitespace characters with a single space.
163
163
  # @return String
164
164
  def clean!
165
- @wrapped_string = @wrapped_string.gsub(/\A\-|\-\z/, '').gsub(/\s+/u, ' ').strip
165
+ @wrapped_string = @wrapped_string.gsub(/\A\-|\-\z/, "").gsub(/\s+/u, " ").strip
166
166
  end
167
167
 
168
168
  # Lowercases the string. Note that this works for Unicode strings,
@@ -198,13 +198,9 @@ module FriendlyId
198
198
  # @param config [FriendlyId::Configuration]
199
199
  # @return String
200
200
  def normalize_for!(config)
201
- if config.normalizer?
202
- @wrapped_string = config.normalizer.call(to_s)
203
- else
204
- approximate_ascii! if config.approximate_ascii?
205
- to_ascii! if config.strip_non_ascii?
206
- normalize!
207
- end
201
+ approximate_ascii! if config.approximate_ascii?
202
+ to_ascii! if config.strip_non_ascii?
203
+ normalize!
208
204
  end
209
205
 
210
206
  alias :normalize_utf8 :normalize rescue NoMethodError
@@ -231,7 +227,12 @@ module FriendlyId
231
227
  # Delete any non-ascii characters.
232
228
  # @return String
233
229
  def to_ascii!
234
- @wrapped_string = normalize_utf8(:c).unpack("U*").reject {|char| char > 127}.pack("U*")
230
+ if ">= 1.9".respond_to?(:force_encoding)
231
+ @wrapped_string.encode!("ASCII", :invalid => :replace, :undef => :replace,
232
+ :replace => "")
233
+ else
234
+ @wrapped_string = tidy_bytes.normalize_utf8(:c).unpack("U*").reject {|char| char > 127}.pack("U*")
235
+ end
235
236
  end
236
237
 
237
238
  # Upper-cases the string. Note that this works for Unicode strings,
@@ -257,7 +258,7 @@ module FriendlyId
257
258
  # Replaces whitespace with dashes ("-").
258
259
  # @return String
259
260
  def with_dashes!
260
- @wrapped_string = @wrapped_string.gsub(/[\s\-]+/u, '-')
261
+ @wrapped_string = @wrapped_string.gsub(/[\s\-]+/u, "-")
261
262
  end
262
263
 
263
264
  %w[approximate_ascii clean downcase word_chars normalize normalize_for to_ascii
@@ -55,7 +55,7 @@ module FriendlyId
55
55
 
56
56
  # The friendly id.
57
57
  def friendly_id
58
- slug.to_friendly_id
58
+ slug.to_friendly_id if slug?
59
59
  end
60
60
 
61
61
  # Clean up the string before setting it as the friendly_id. You can override
@@ -75,8 +75,11 @@ module FriendlyId
75
75
 
76
76
  # Get the processed string used as the basis of the friendly id.
77
77
  def slug_text
78
- text = normalize_friendly_id(SlugString.new(send(friendly_id_config.method)))
79
- SlugString.new(text.to_s).validate_for!(friendly_id_config).to_s
78
+ base = send(friendly_id_config.method)
79
+ unless base.nil? && friendly_id_config.allow_nil?
80
+ text = normalize_friendly_id(SlugString.new(base))
81
+ SlugString.new(text.to_s).validate_for!(friendly_id_config).to_s
82
+ end
80
83
  end
81
84
 
82
85
  # Has the slug text changed?
@@ -87,7 +90,11 @@ module FriendlyId
87
90
  # Has the basis of our friendly id changed, requiring the generation of a
88
91
  # new slug?
89
92
  def new_slug_needed?
90
- !slug? || slug_text_changed?
93
+ if friendly_id_config.allow_nil?
94
+ (!slug? && !slug_text.blank?) || (slug? && slug_text_changed?)
95
+ else
96
+ !slug? || slug_text_changed?
97
+ end
91
98
  end
92
99
 
93
100
  end
@@ -18,7 +18,6 @@ module FriendlyId
18
18
 
19
19
  def teardown
20
20
  klass.send delete_all_method
21
- # other_class.delete_all
22
21
  end
23
22
 
24
23
  def instance
@@ -41,6 +40,10 @@ module FriendlyId
41
40
  raise NotImplementedError
42
41
  end
43
42
 
43
+ def update_method
44
+ raise NotImplementedError
45
+ end
46
+
44
47
  def validation_exceptions
45
48
  return RuntimeError
46
49
  end
@@ -49,7 +52,7 @@ module FriendlyId
49
52
  assert_not_nil klass.friendly_id_config
50
53
  end
51
54
 
52
- test "instances should have a friendly id" do
55
+ test "instances should have a friendly id by default" do
53
56
  assert_not_nil instance.friendly_id
54
57
  end
55
58
 
@@ -69,6 +72,11 @@ module FriendlyId
69
72
  assert_equal instance, klass.send(find_method, instance.id.to_s)
70
73
  end
71
74
 
75
+ test "instances should be findable by a numeric friendly_id" do
76
+ instance = klass.send(create_method, :name => "206")
77
+ assert_equal instance, klass.send(find_method, instance.friendly_id)
78
+ end
79
+
72
80
  test "creation should raise an error if the friendly_id text is reserved" do
73
81
  assert_raise(*[validation_exceptions].flatten) do
74
82
  klass.send(create_method, :name => "new")
@@ -87,12 +95,17 @@ module FriendlyId
87
95
  end
88
96
  end
89
97
 
90
- test "creation should raise an error if the friendly_id text is nil" do
98
+ test "creation should raise an error if the friendly_id text is nil and allow_nil is false" do
91
99
  assert_raise(*[validation_exceptions].flatten) do
92
100
  klass.send(create_method, :name => nil)
93
101
  end
94
102
  end
95
103
 
104
+ test "creation should succeed if the friendly_id text is nil and allow_nil is true" do
105
+ klass.friendly_id_config.stubs(:allow_nil?).returns(true)
106
+ assert klass.send(create_method, :name => nil)
107
+ end
108
+
96
109
  test "should allow the same friendly_id across models" do
97
110
  other_instance = other_class.send(create_method, :name => instance.name)
98
111
  assert_equal other_instance.friendly_id, instance.friendly_id
@@ -100,6 +113,18 @@ module FriendlyId
100
113
 
101
114
  end
102
115
 
116
+ module Simple
117
+
118
+ test "should allow friendly_id to be nillable if allow_nil is true" do
119
+ klass.friendly_id_config.stubs(:allow_nil?).returns(true)
120
+ instance = klass.send(create_method, :name => "hello")
121
+ assert instance.friendly_id
122
+ instance.name = nil
123
+ assert instance.send(save_method)
124
+ end
125
+
126
+ end
127
+
103
128
  # Tests for any model that implements slugs.
104
129
  module Slugged
105
130
 
@@ -152,6 +177,22 @@ module FriendlyId
152
177
  assert_equal instance, klass.find(old_friendly_id)
153
178
  end
154
179
 
180
+ test "should not create a slug when allow_nil is true and friendy_id text is blank" do
181
+ klass.friendly_id_config.stubs(:allow_nil?).returns(true)
182
+ instance = klass.send(create_method, :name => nil)
183
+ assert_nil instance.slug
184
+ end
185
+
186
+ test "should not allow friendly_id to be nillable even if allow_nil is true" do
187
+ klass.friendly_id_config.stubs(:allow_nil?).returns(true)
188
+ instance = klass.send(create_method, :name => "hello")
189
+ assert instance.friendly_id
190
+ instance.name = nil
191
+ assert_raise(*[validation_exceptions].flatten) do
192
+ instance.send(save_method)
193
+ end
194
+ end
195
+
155
196
  end
156
197
 
157
198
  # Tests for FriendlyId::Status.
@@ -164,7 +205,7 @@ module FriendlyId
164
205
  assert status.numeric?
165
206
  end
166
207
  end
167
-
208
+
168
209
  # Tests for FriendlyId::Status for a model that uses slugs.
169
210
  module SluggedStatus
170
211
 
@@ -244,4 +285,4 @@ module FriendlyId
244
285
 
245
286
  end
246
287
  end
247
- end
288
+ end