friendly_id 4.0.0.beta7 → 4.0.0.beta8
Sign up to get free protection for your applications and to get access to all the features.
- data/Changelog.md +3 -3
- data/README.md +6 -4
- data/lib/friendly_id.rb +1 -1
- data/lib/generators/friendly_id_generator.rb +5 -6
- metadata +15 -29
- data/lib/friendly_id/active_record.rb +0 -56
- data/lib/friendly_id/active_record_adapter/configuration.rb +0 -68
- data/lib/friendly_id/active_record_adapter/relation.rb +0 -173
- data/lib/friendly_id/active_record_adapter/simple_model.rb +0 -62
- data/lib/friendly_id/active_record_adapter/slug.rb +0 -84
- data/lib/friendly_id/active_record_adapter/slugged_model.rb +0 -110
- data/lib/friendly_id/active_record_adapter/tasks.rb +0 -72
- data/lib/friendly_id/datamapper.rb +0 -5
- data/lib/friendly_id/railtie.rb +0 -22
- data/lib/friendly_id/sequel.rb +0 -5
- data/lib/friendly_id/slug_string.rb +0 -25
- data/lib/friendly_id/status.rb +0 -35
- data/lib/friendly_id/test.rb +0 -364
- data/lib/friendly_id/version.rb +0 -9
data/Changelog.md
CHANGED
@@ -178,11 +178,11 @@ fork, then this upgrade may causes issues.
|
|
178
178
|
* New option to pass arguments to `FriendlyId::SlugString#approximate_ascii!`,
|
179
179
|
allowing custom approximations specific to German or Spanish.
|
180
180
|
* FriendlyId now queries against the cached_slug column, which improves performance.
|
181
|
-
*
|
181
|
+
* FriendlyId::SlugString class added, allowing finer-grained control over
|
182
182
|
Unicode friendly_id strings.
|
183
|
-
*
|
183
|
+
* FriendlyId::Configuration class added, offering more flexible/hackable
|
184
184
|
options.
|
185
|
-
* FriendlyId now raises subclasses of
|
185
|
+
* FriendlyId now raises subclasses of FriendlyId::SlugGenerationError
|
186
186
|
depending on the error context.
|
187
187
|
* Simple models now correctly validate friendly_id length.
|
188
188
|
* Passing block into FriendlyId deprecated in favor of overriding
|
data/README.md
CHANGED
@@ -24,8 +24,10 @@ FriendlyId is compatible with Active Record **3.0** and **3.1**.
|
|
24
24
|
|
25
25
|
## Version 4.x
|
26
26
|
|
27
|
-
FriendlyId 4.x introduces many changes incompatible with 3.x. If you're
|
28
|
-
please read the
|
27
|
+
FriendlyId 4.x introduces many changes incompatible with 3.x. If you're
|
28
|
+
upgrading, please [read the
|
29
|
+
docs](http://norman.github.com/friendly_id/file.WhatsNew.html) to see what's
|
30
|
+
new.
|
29
31
|
|
30
32
|
## Rails Quickstart
|
31
33
|
|
@@ -35,7 +37,7 @@ please read the docs to see what's new.
|
|
35
37
|
|
36
38
|
cd my_app
|
37
39
|
|
38
|
-
gem "friendly_id", "~> 4.0.0.
|
40
|
+
gem "friendly_id", "~> 4.0.0.beta8"
|
39
41
|
|
40
42
|
|
41
43
|
rails generate scaffold user name:string slug:string
|
@@ -60,7 +62,7 @@ please read the docs to see what's new.
|
|
60
62
|
## Docs
|
61
63
|
|
62
64
|
The current docs can be found
|
63
|
-
[here](http://
|
65
|
+
[here](http://norman.github.com/friendly_id/)
|
64
66
|
|
65
67
|
## Benchmarks
|
66
68
|
|
data/lib/friendly_id.rb
CHANGED
@@ -1,16 +1,16 @@
|
|
1
1
|
require 'rails/generators'
|
2
2
|
require 'rails/generators/migration'
|
3
3
|
|
4
|
+
# This generator adds a migration for the {FriendlyId::History
|
5
|
+
# FriendlyId::History} addon.
|
4
6
|
class FriendlyIdGenerator < Rails::Generators::Base
|
5
|
-
|
6
7
|
include Rails::Generators::Migration
|
7
8
|
|
8
|
-
source_root File.expand_path('
|
9
|
-
|
10
|
-
class_option :"skip-migration", :type => :boolean, :desc => "Don't generate a migration for the slugs table"
|
9
|
+
source_root File.expand_path('../../friendly_id', __FILE__)
|
11
10
|
|
11
|
+
# Copies the migration template to db/migrate.
|
12
12
|
def copy_files(*args)
|
13
|
-
migration_template '
|
13
|
+
migration_template 'migration.rb', 'db/migrate/create_friendly_id_slugs.rb'
|
14
14
|
end
|
15
15
|
|
16
16
|
# Taken from ActiveRecord's migration generator
|
@@ -21,5 +21,4 @@ class FriendlyIdGenerator < Rails::Generators::Base
|
|
21
21
|
"%.3d" % (current_migration_number(dirname) + 1)
|
22
22
|
end
|
23
23
|
end
|
24
|
-
|
25
24
|
end
|
metadata
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: friendly_id
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 4.0.0.
|
4
|
+
version: 4.0.0.beta8
|
5
5
|
prerelease: 6
|
6
6
|
platform: ruby
|
7
7
|
authors:
|
@@ -14,7 +14,7 @@ default_executable:
|
|
14
14
|
dependencies:
|
15
15
|
- !ruby/object:Gem::Dependency
|
16
16
|
name: activerecord
|
17
|
-
requirement: &
|
17
|
+
requirement: &70172556099120 !ruby/object:Gem::Requirement
|
18
18
|
none: false
|
19
19
|
requirements:
|
20
20
|
- - ~>
|
@@ -22,10 +22,10 @@ dependencies:
|
|
22
22
|
version: '3.0'
|
23
23
|
type: :development
|
24
24
|
prerelease: false
|
25
|
-
version_requirements: *
|
25
|
+
version_requirements: *70172556099120
|
26
26
|
- !ruby/object:Gem::Dependency
|
27
27
|
name: sqlite3
|
28
|
-
requirement: &
|
28
|
+
requirement: &70172556098620 !ruby/object:Gem::Requirement
|
29
29
|
none: false
|
30
30
|
requirements:
|
31
31
|
- - ~>
|
@@ -33,10 +33,10 @@ dependencies:
|
|
33
33
|
version: '1.3'
|
34
34
|
type: :development
|
35
35
|
prerelease: false
|
36
|
-
version_requirements: *
|
36
|
+
version_requirements: *70172556098620
|
37
37
|
- !ruby/object:Gem::Dependency
|
38
38
|
name: minitest
|
39
|
-
requirement: &
|
39
|
+
requirement: &70172556098140 !ruby/object:Gem::Requirement
|
40
40
|
none: false
|
41
41
|
requirements:
|
42
42
|
- - ~>
|
@@ -44,10 +44,10 @@ dependencies:
|
|
44
44
|
version: 2.4.0
|
45
45
|
type: :development
|
46
46
|
prerelease: false
|
47
|
-
version_requirements: *
|
47
|
+
version_requirements: *70172556098140
|
48
48
|
- !ruby/object:Gem::Dependency
|
49
49
|
name: mocha
|
50
|
-
requirement: &
|
50
|
+
requirement: &70172556097640 !ruby/object:Gem::Requirement
|
51
51
|
none: false
|
52
52
|
requirements:
|
53
53
|
- - ~>
|
@@ -55,10 +55,10 @@ dependencies:
|
|
55
55
|
version: 0.9.12
|
56
56
|
type: :development
|
57
57
|
prerelease: false
|
58
|
-
version_requirements: *
|
58
|
+
version_requirements: *70172556097640
|
59
59
|
- !ruby/object:Gem::Dependency
|
60
60
|
name: ffaker
|
61
|
-
requirement: &
|
61
|
+
requirement: &70172556097180 !ruby/object:Gem::Requirement
|
62
62
|
none: false
|
63
63
|
requirements:
|
64
64
|
- - ~>
|
@@ -66,10 +66,10 @@ dependencies:
|
|
66
66
|
version: 1.8.0
|
67
67
|
type: :development
|
68
68
|
prerelease: false
|
69
|
-
version_requirements: *
|
69
|
+
version_requirements: *70172556097180
|
70
70
|
- !ruby/object:Gem::Dependency
|
71
71
|
name: maruku
|
72
|
-
requirement: &
|
72
|
+
requirement: &70172556096720 !ruby/object:Gem::Requirement
|
73
73
|
none: false
|
74
74
|
requirements:
|
75
75
|
- - ~>
|
@@ -77,10 +77,10 @@ dependencies:
|
|
77
77
|
version: 0.6.0
|
78
78
|
type: :development
|
79
79
|
prerelease: false
|
80
|
-
version_requirements: *
|
80
|
+
version_requirements: *70172556096720
|
81
81
|
- !ruby/object:Gem::Dependency
|
82
82
|
name: yard
|
83
|
-
requirement: &
|
83
|
+
requirement: &70172556096260 !ruby/object:Gem::Requirement
|
84
84
|
none: false
|
85
85
|
requirements:
|
86
86
|
- - ~>
|
@@ -88,7 +88,7 @@ dependencies:
|
|
88
88
|
version: 0.7.2
|
89
89
|
type: :development
|
90
90
|
prerelease: false
|
91
|
-
version_requirements: *
|
91
|
+
version_requirements: *70172556096260
|
92
92
|
description: ! 'FriendlyId is the "Swiss Army bulldozer" of slugging and permalink
|
93
93
|
plugins for
|
94
94
|
|
@@ -120,32 +120,18 @@ files:
|
|
120
120
|
- gemfiles/Gemfile.rails-3.1.rb
|
121
121
|
- gemfiles/Gemfile.rails-3.1.rb.lock
|
122
122
|
- lib/friendly_id.rb
|
123
|
-
- lib/friendly_id/active_record.rb
|
124
|
-
- lib/friendly_id/active_record_adapter/configuration.rb
|
125
|
-
- lib/friendly_id/active_record_adapter/relation.rb
|
126
|
-
- lib/friendly_id/active_record_adapter/simple_model.rb
|
127
|
-
- lib/friendly_id/active_record_adapter/slug.rb
|
128
|
-
- lib/friendly_id/active_record_adapter/slugged_model.rb
|
129
|
-
- lib/friendly_id/active_record_adapter/tasks.rb
|
130
123
|
- lib/friendly_id/base.rb
|
131
124
|
- lib/friendly_id/configuration.rb
|
132
|
-
- lib/friendly_id/datamapper.rb
|
133
125
|
- lib/friendly_id/finder_methods.rb
|
134
126
|
- lib/friendly_id/history.rb
|
135
127
|
- lib/friendly_id/migration.rb
|
136
128
|
- lib/friendly_id/model.rb
|
137
129
|
- lib/friendly_id/object_utils.rb
|
138
|
-
- lib/friendly_id/railtie.rb
|
139
130
|
- lib/friendly_id/reserved.rb
|
140
131
|
- lib/friendly_id/scoped.rb
|
141
|
-
- lib/friendly_id/sequel.rb
|
142
132
|
- lib/friendly_id/slug.rb
|
143
133
|
- lib/friendly_id/slug_sequencer.rb
|
144
|
-
- lib/friendly_id/slug_string.rb
|
145
134
|
- lib/friendly_id/slugged.rb
|
146
|
-
- lib/friendly_id/status.rb
|
147
|
-
- lib/friendly_id/test.rb
|
148
|
-
- lib/friendly_id/version.rb
|
149
135
|
- lib/generators/friendly_id_generator.rb
|
150
136
|
- test/base_test.rb
|
151
137
|
- test/configuration_test.rb
|
@@ -1,56 +0,0 @@
|
|
1
|
-
module FriendlyId
|
2
|
-
|
3
|
-
module ActiveRecordAdapter
|
4
|
-
|
5
|
-
include FriendlyId::Base
|
6
|
-
|
7
|
-
def has_friendly_id(method, options = {})
|
8
|
-
|
9
|
-
class_attribute :friendly_id_config
|
10
|
-
self.friendly_id_config = Configuration.new(self, method, options)
|
11
|
-
|
12
|
-
if friendly_id_config.use_slug?
|
13
|
-
include SluggedModel
|
14
|
-
else
|
15
|
-
include SimpleModel
|
16
|
-
end
|
17
|
-
end
|
18
|
-
|
19
|
-
private
|
20
|
-
|
21
|
-
# Prevent the cached_slug column from being accidentally or maliciously
|
22
|
-
# overwritten. Note that +attr_protected+ is used to protect the cached_slug
|
23
|
-
# column, unless you have already invoked +attr_accessible+. So if you
|
24
|
-
# wish to use +attr_accessible+, you must invoke it BEFORE you invoke
|
25
|
-
# {#has_friendly_id} in your class.
|
26
|
-
def protect_friendly_id_attributes
|
27
|
-
# only protect the column if the class is not already using attr_accessible
|
28
|
-
unless accessible_attributes.present?
|
29
|
-
if friendly_id_config.custom_cache_column?
|
30
|
-
attr_protected friendly_id_config.cache_column
|
31
|
-
end
|
32
|
-
attr_protected :cached_slug
|
33
|
-
end
|
34
|
-
end
|
35
|
-
|
36
|
-
end
|
37
|
-
end
|
38
|
-
|
39
|
-
require "friendly_id/active_record_adapter/relation"
|
40
|
-
require "friendly_id/active_record_adapter/configuration"
|
41
|
-
require "friendly_id/active_record_adapter/simple_model"
|
42
|
-
require "friendly_id/active_record_adapter/slugged_model"
|
43
|
-
require "friendly_id/active_record_adapter/slug"
|
44
|
-
require "friendly_id/active_record_adapter/tasks"
|
45
|
-
|
46
|
-
module ActiveRecord
|
47
|
-
class Base
|
48
|
-
extend FriendlyId::ActiveRecordAdapter
|
49
|
-
end
|
50
|
-
|
51
|
-
class Relation
|
52
|
-
alias find_one_without_friendly find_one
|
53
|
-
alias find_some_without_friendly find_some
|
54
|
-
include FriendlyId::ActiveRecordAdapter::Relation
|
55
|
-
end
|
56
|
-
end
|
@@ -1,68 +0,0 @@
|
|
1
|
-
module FriendlyId
|
2
|
-
|
3
|
-
module ActiveRecordAdapter
|
4
|
-
|
5
|
-
# Extends FriendlyId::Configuration with some implementation details and
|
6
|
-
# features specific to ActiveRecord.
|
7
|
-
class Configuration < FriendlyId::Configuration
|
8
|
-
|
9
|
-
# The column used to cache the friendly_id string. If no column is specified,
|
10
|
-
# FriendlyId will look for a column named +cached_slug+ and use it automatically
|
11
|
-
# if it exists. If for some reason you have a column named +cached_slug+
|
12
|
-
# but don't want FriendlyId to modify it, pass the option
|
13
|
-
# +:cache_column => false+ to {FriendlyId::ActiveRecordAdapter#has_friendly_id has_friendly_id}.
|
14
|
-
attr_accessor :cache_column
|
15
|
-
|
16
|
-
# An array of classes for which the configured class serves as a
|
17
|
-
# FriendlyId scope.
|
18
|
-
attr_reader :child_scopes
|
19
|
-
|
20
|
-
attr_reader :custom_cache_column
|
21
|
-
|
22
|
-
def cache_column
|
23
|
-
return @cache_column if defined?(@cache_column)
|
24
|
-
@cache_column = autodiscover_cache_column
|
25
|
-
end
|
26
|
-
|
27
|
-
def cache_column?
|
28
|
-
!! cache_column
|
29
|
-
end
|
30
|
-
|
31
|
-
def cache_column=(cache_column)
|
32
|
-
@cache_column = cache_column
|
33
|
-
@custom_cache_column = cache_column
|
34
|
-
end
|
35
|
-
|
36
|
-
def child_scopes
|
37
|
-
@child_scopes ||= associated_friendly_classes.select do |klass|
|
38
|
-
klass.friendly_id_config.scopes_over?(configured_class)
|
39
|
-
end
|
40
|
-
end
|
41
|
-
|
42
|
-
def custom_cache_column?
|
43
|
-
!! custom_cache_column
|
44
|
-
end
|
45
|
-
|
46
|
-
def scope_for(record)
|
47
|
-
return nil unless scope?
|
48
|
-
record.send(scope).nil? ? nil : record.send(scope).to_param
|
49
|
-
end
|
50
|
-
|
51
|
-
def scopes_over?(klass)
|
52
|
-
scope? && scope == klass.to_s.underscore.to_sym
|
53
|
-
end
|
54
|
-
|
55
|
-
private
|
56
|
-
|
57
|
-
def autodiscover_cache_column
|
58
|
-
:cached_slug if configured_class.columns.any? { |column| column.name == 'cached_slug' }
|
59
|
-
end
|
60
|
-
|
61
|
-
def associated_friendly_classes
|
62
|
-
configured_class.reflect_on_all_associations.compact.select { |assoc|
|
63
|
-
!assoc.options[:polymorphic] && assoc.klass.respond_to?(:friendly_id_config)
|
64
|
-
}.map(&:klass)
|
65
|
-
end
|
66
|
-
end
|
67
|
-
end
|
68
|
-
end
|
@@ -1,173 +0,0 @@
|
|
1
|
-
module FriendlyId
|
2
|
-
module ActiveRecordAdapter
|
3
|
-
module Relation
|
4
|
-
|
5
|
-
class Find
|
6
|
-
extend Forwardable
|
7
|
-
|
8
|
-
attr :relation
|
9
|
-
attr :ids
|
10
|
-
alias id ids
|
11
|
-
|
12
|
-
def_delegators :relation, :arel, :arel_table, :klass, :limit_value, :offset_value, :where
|
13
|
-
def_delegators :klass, :connection, :friendly_id_config
|
14
|
-
alias fc friendly_id_config
|
15
|
-
|
16
|
-
def initialize(relation, ids)
|
17
|
-
@relation = relation
|
18
|
-
@ids = ids
|
19
|
-
end
|
20
|
-
|
21
|
-
def find_one
|
22
|
-
if fc.cache_column?
|
23
|
-
find_one_with_cached_slug
|
24
|
-
elsif fc.use_slugs?
|
25
|
-
find_one_with_slug
|
26
|
-
else
|
27
|
-
find_one_without_slug
|
28
|
-
end
|
29
|
-
end
|
30
|
-
|
31
|
-
def find_some
|
32
|
-
ids = @ids.compact.uniq.map {|id| id.respond_to?(:friendly_id_config) ? id.id : id}
|
33
|
-
friendly_ids, unfriendly_ids = ids.partition {|id| id.friendly_id?}
|
34
|
-
return if friendly_ids.empty?
|
35
|
-
records = friendly_records(friendly_ids, unfriendly_ids).each do |record|
|
36
|
-
record.friendly_id_status.name = ids
|
37
|
-
end
|
38
|
-
validate_expected_size!(ids, records)
|
39
|
-
end
|
40
|
-
|
41
|
-
private
|
42
|
-
|
43
|
-
def assign_status
|
44
|
-
return unless @result
|
45
|
-
name, seq = id.to_s.parse_friendly_id
|
46
|
-
@result.friendly_id_status.name = name
|
47
|
-
@result.friendly_id_status.sequence = seq if fc.use_slugs?
|
48
|
-
@result
|
49
|
-
end
|
50
|
-
|
51
|
-
def find_one_without_slug
|
52
|
-
@result = where(fc.column => id).first
|
53
|
-
assign_status
|
54
|
-
end
|
55
|
-
|
56
|
-
def find_one_with_cached_slug
|
57
|
-
@result = where(fc.cache_column => id).first
|
58
|
-
assign_status or find_one_with_slug
|
59
|
-
end
|
60
|
-
|
61
|
-
def find_one_with_slug
|
62
|
-
sluggable_ids = sluggable_ids_for([id])
|
63
|
-
|
64
|
-
if sluggable_ids.size > 1 && fc.scope?
|
65
|
-
return relation.where(primary_key_column.in(sluggable_ids)).first
|
66
|
-
end
|
67
|
-
|
68
|
-
sluggable_id = sluggable_ids.first
|
69
|
-
|
70
|
-
if sluggable_id
|
71
|
-
name, seq = id.to_s.parse_friendly_id
|
72
|
-
record = relation.send(:find_one_without_friendly, sluggable_id)
|
73
|
-
record.friendly_id_status.name = name
|
74
|
-
record.friendly_id_status.sequence = seq
|
75
|
-
record
|
76
|
-
else
|
77
|
-
relation.send(:find_one_without_friendly, id)
|
78
|
-
end
|
79
|
-
end
|
80
|
-
|
81
|
-
def friendly_records(friendly_ids, unfriendly_ids)
|
82
|
-
use_slugs_table = fc.use_slugs? && (!fc.cache_column?)
|
83
|
-
return find_some_using_slug(friendly_ids, unfriendly_ids) if use_slugs_table
|
84
|
-
column = fc.cache_column || fc.column
|
85
|
-
friendly = arel_table[column].in(friendly_ids)
|
86
|
-
unfriendly = primary_key_column.in unfriendly_ids
|
87
|
-
if friendly_ids.present? && unfriendly_ids.present?
|
88
|
-
where(friendly.or(unfriendly))
|
89
|
-
else
|
90
|
-
where(friendly)
|
91
|
-
end
|
92
|
-
end
|
93
|
-
|
94
|
-
def find_some_using_slug(friendly_ids, unfriendly_ids)
|
95
|
-
ids = [unfriendly_ids + sluggable_ids_for(friendly_ids)].flatten.uniq
|
96
|
-
where(primary_key_column.in(ids))
|
97
|
-
end
|
98
|
-
|
99
|
-
def sluggable_ids_for(ids)
|
100
|
-
return [] if ids.empty?
|
101
|
-
fragment = "(slugs.sluggable_type = %s AND slugs.name = %s AND slugs.sequence = %d)"
|
102
|
-
conditions = ids.inject(nil) do |clause, id|
|
103
|
-
name, seq = id.parse_friendly_id
|
104
|
-
string = fragment % [connection.quote(klass.base_class.name), connection.quote(name), seq]
|
105
|
-
clause ? clause + " OR #{string}" : string
|
106
|
-
end
|
107
|
-
sql = "SELECT sluggable_id FROM slugs WHERE (%s)" % conditions
|
108
|
-
connection.select_values sql
|
109
|
-
end
|
110
|
-
|
111
|
-
def validate_expected_size!(ids, result)
|
112
|
-
expected_size =
|
113
|
-
if limit_value && ids.size > limit_value
|
114
|
-
limit_value
|
115
|
-
else
|
116
|
-
ids.size
|
117
|
-
end
|
118
|
-
|
119
|
-
# 11 ids with limit 3, offset 9 should give 2 results.
|
120
|
-
if offset_value && (ids.size - offset_value < expected_size)
|
121
|
-
expected_size = ids.size - offset_value
|
122
|
-
end
|
123
|
-
|
124
|
-
if result.size == expected_size
|
125
|
-
result
|
126
|
-
else
|
127
|
-
conditions = arel.send(:where_clauses).join(', ')
|
128
|
-
conditions = " [WHERE #{conditions}]" if conditions.present?
|
129
|
-
error = "Couldn't find all #{klass.name.pluralize} with IDs "
|
130
|
-
error << "(#{ids.join(", ")})#{conditions} (found #{result.size} results, but was looking for #{expected_size})"
|
131
|
-
raise ActiveRecord::RecordNotFound, error
|
132
|
-
end
|
133
|
-
end
|
134
|
-
|
135
|
-
def primary_key_column
|
136
|
-
if relation.primary_key.is_a?(String)
|
137
|
-
arel_table[relation.primary_key]
|
138
|
-
else
|
139
|
-
arel_table[relation.primary_key.name]
|
140
|
-
end
|
141
|
-
end
|
142
|
-
end
|
143
|
-
|
144
|
-
def apply_finder_options(options)
|
145
|
-
if options[:scope]
|
146
|
-
raise "The :scope finder option has been removed from FriendlyId 3.2.0 " +
|
147
|
-
"https://github.com/norman/friendly_id/issues#issue/88"
|
148
|
-
end
|
149
|
-
super
|
150
|
-
end
|
151
|
-
|
152
|
-
protected
|
153
|
-
|
154
|
-
def find_one(id)
|
155
|
-
begin
|
156
|
-
return super if !klass.uses_friendly_id? or id.unfriendly_id?
|
157
|
-
find = Find.new(self, id)
|
158
|
-
find.find_one or super
|
159
|
-
end
|
160
|
-
end
|
161
|
-
|
162
|
-
def find_some(ids)
|
163
|
-
return super unless klass.uses_friendly_id?
|
164
|
-
Find.new(self, ids).find_some or begin
|
165
|
-
# A change in Arel 2.0.x causes find_some to fail with arrays of instances; not sure why.
|
166
|
-
# This is an emergency, temporary fix.
|
167
|
-
ids = ids.map {|id| (id.respond_to?(:friendly_id_config) ? id.id : id)}
|
168
|
-
super
|
169
|
-
end
|
170
|
-
end
|
171
|
-
end
|
172
|
-
end
|
173
|
-
end
|
@@ -1,62 +0,0 @@
|
|
1
|
-
module FriendlyId
|
2
|
-
module ActiveRecordAdapter
|
3
|
-
|
4
|
-
module SimpleModel
|
5
|
-
|
6
|
-
def self.included(base)
|
7
|
-
base.class_eval do
|
8
|
-
column = friendly_id_config.column
|
9
|
-
validate :validate_friendly_id, :unless => :skip_friendly_id_validations
|
10
|
-
validates_presence_of column, :unless => :skip_friendly_id_validations
|
11
|
-
validates_length_of column, :maximum => friendly_id_config.max_length, :unless => :skip_friendly_id_validations
|
12
|
-
after_update :update_scopes
|
13
|
-
end
|
14
|
-
end
|
15
|
-
|
16
|
-
# Get the {FriendlyId::Status} after the find has been performed.
|
17
|
-
def friendly_id_status
|
18
|
-
@friendly_id_status ||= Status.new :record => self
|
19
|
-
end
|
20
|
-
|
21
|
-
# Returns the friendly_id.
|
22
|
-
def friendly_id
|
23
|
-
send friendly_id_config.column
|
24
|
-
end
|
25
|
-
alias best_id friendly_id
|
26
|
-
|
27
|
-
# Returns the friendly id, or if none is available, the numeric id.
|
28
|
-
def to_param
|
29
|
-
(friendly_id || id).to_s
|
30
|
-
end
|
31
|
-
|
32
|
-
private
|
33
|
-
|
34
|
-
# The old and new values for the friendly_id column.
|
35
|
-
def friendly_id_changes
|
36
|
-
changes[friendly_id_config.column.to_s]
|
37
|
-
end
|
38
|
-
|
39
|
-
# Update the slugs for any model that is using this model as its
|
40
|
-
# FriendlyId scope.
|
41
|
-
def update_scopes
|
42
|
-
if changes = friendly_id_changes
|
43
|
-
friendly_id_config.child_scopes.each do |klass|
|
44
|
-
Slug.update_all "scope = '#{changes[1]}'", ["sluggable_type = ? AND scope = ?", klass.to_s, changes[0]]
|
45
|
-
end
|
46
|
-
end
|
47
|
-
end
|
48
|
-
|
49
|
-
def skip_friendly_id_validations
|
50
|
-
friendly_id.nil? && friendly_id_config.allow_nil?
|
51
|
-
end
|
52
|
-
|
53
|
-
def validate_friendly_id
|
54
|
-
if result = friendly_id_config.reserved_error_message(friendly_id)
|
55
|
-
self.errors.add(*result)
|
56
|
-
return false
|
57
|
-
end
|
58
|
-
end
|
59
|
-
|
60
|
-
end
|
61
|
-
end
|
62
|
-
end
|
@@ -1,84 +0,0 @@
|
|
1
|
-
# A Slug is a unique, human-friendly identifier for an ActiveRecord.
|
2
|
-
class Slug < ::ActiveRecord::Base
|
3
|
-
attr_writer :sluggable
|
4
|
-
attr_accessible :name, :scope, :sluggable, :sequence
|
5
|
-
table_name = "slugs"
|
6
|
-
before_save :enable_name_reversion, :set_sequence
|
7
|
-
validate :validate_name
|
8
|
-
scope :similar_to, lambda {|slug| {:conditions => {
|
9
|
-
:name => slug.name,
|
10
|
-
:scope => slug.scope,
|
11
|
-
:sluggable_type => slug.sluggable_type
|
12
|
-
},
|
13
|
-
:order => "sequence ASC"
|
14
|
-
}
|
15
|
-
}
|
16
|
-
|
17
|
-
def sluggable
|
18
|
-
sluggable_id && !@sluggable and begin
|
19
|
-
klass = sluggable_type.constantize
|
20
|
-
klass.send(:with_exclusive_scope) do
|
21
|
-
@sluggable = klass.find(sluggable_id)
|
22
|
-
end
|
23
|
-
end
|
24
|
-
@sluggable
|
25
|
-
end
|
26
|
-
|
27
|
-
# Whether this slug is the most recent of its owner's slugs.
|
28
|
-
def current?
|
29
|
-
sluggable.slug == self
|
30
|
-
end
|
31
|
-
|
32
|
-
def outdated?
|
33
|
-
!current?
|
34
|
-
end
|
35
|
-
|
36
|
-
def to_friendly_id
|
37
|
-
sequence > 1 ? friendly_id_with_sequence : name
|
38
|
-
end
|
39
|
-
|
40
|
-
# Raise a FriendlyId::SlugGenerationError if the slug name is blank.
|
41
|
-
def validate_name
|
42
|
-
if name.blank?
|
43
|
-
raise FriendlyId::BlankError.new("slug.name can not be blank.")
|
44
|
-
end
|
45
|
-
end
|
46
|
-
|
47
|
-
def save(*args)
|
48
|
-
persisted? && !scope_changed? ? true : super
|
49
|
-
end
|
50
|
-
|
51
|
-
def save!(*args)
|
52
|
-
persisted? && !scope_changed? ? true : super
|
53
|
-
end
|
54
|
-
|
55
|
-
private
|
56
|
-
|
57
|
-
# If we're renaming back to a previously used friendly_id, delete the
|
58
|
-
# slug so that we can recycle the name without having to use a sequence.
|
59
|
-
def enable_name_reversion
|
60
|
-
sluggable.slugs.find_all_by_name_and_scope(name, scope).each { |slug| slug.destroy }
|
61
|
-
end
|
62
|
-
|
63
|
-
def friendly_id_with_sequence
|
64
|
-
"#{name}#{separator}#{sequence}"
|
65
|
-
end
|
66
|
-
|
67
|
-
def similar_to_other_slugs?
|
68
|
-
!similar_slugs.empty?
|
69
|
-
end
|
70
|
-
|
71
|
-
def similar_slugs
|
72
|
-
self.class.similar_to(self)
|
73
|
-
end
|
74
|
-
|
75
|
-
def separator
|
76
|
-
sluggable.friendly_id_config.sequence_separator
|
77
|
-
end
|
78
|
-
|
79
|
-
def set_sequence
|
80
|
-
return unless new_record?
|
81
|
-
self.sequence = similar_slugs.last.sequence.succ if similar_to_other_slugs?
|
82
|
-
end
|
83
|
-
|
84
|
-
end
|
@@ -1,110 +0,0 @@
|
|
1
|
-
module FriendlyId
|
2
|
-
module ActiveRecordAdapter
|
3
|
-
module SluggedModel
|
4
|
-
|
5
|
-
def self.included(base)
|
6
|
-
base.class_eval do
|
7
|
-
has_many :slugs, :order => 'id DESC', :as => :sluggable, :dependent => :destroy
|
8
|
-
has_one :slug, :order => 'id DESC', :as => :sluggable, :dependent => :nullify
|
9
|
-
before_save :build_a_slug
|
10
|
-
after_save :set_slug_cache
|
11
|
-
after_update :update_scope
|
12
|
-
after_update :update_dependent_scopes
|
13
|
-
protect_friendly_id_attributes
|
14
|
-
end
|
15
|
-
end
|
16
|
-
|
17
|
-
include FriendlyId::Slugged::Model
|
18
|
-
|
19
|
-
def find_slug(name, sequence)
|
20
|
-
slugs.find_by_name_and_sequence(name, sequence)
|
21
|
-
end
|
22
|
-
|
23
|
-
# Returns the friendly id, or if none is available, the numeric id. Note that this
|
24
|
-
# method will use the cached_slug value if present, unlike {#friendly_id}.
|
25
|
-
def to_param
|
26
|
-
friendly_id_config.cache_column ? to_param_from_cache : to_param_from_slug
|
27
|
-
end
|
28
|
-
|
29
|
-
private
|
30
|
-
|
31
|
-
def scope_changed?
|
32
|
-
friendly_id_config.scope? && send(friendly_id_config.scope).to_param != slug.scope
|
33
|
-
end
|
34
|
-
|
35
|
-
# Respond with the cached value if available.
|
36
|
-
def to_param_from_cache
|
37
|
-
read_attribute(friendly_id_config.cache_column) || id.to_s
|
38
|
-
end
|
39
|
-
|
40
|
-
# Respond with the slugged value if available.
|
41
|
-
def to_param_from_slug
|
42
|
-
slug? ? slug.to_friendly_id : id.to_s
|
43
|
-
end
|
44
|
-
|
45
|
-
# Build the new slug using the generated friendly id.
|
46
|
-
def build_a_slug
|
47
|
-
return unless new_slug_needed?
|
48
|
-
self.slug = slugs.build :name => slug_text.to_s, :scope => friendly_id_config.scope_for(self),
|
49
|
-
:sluggable => self
|
50
|
-
@new_friendly_id = self.slug.to_friendly_id
|
51
|
-
end
|
52
|
-
|
53
|
-
# Reset the cached friendly_id?
|
54
|
-
def new_cache_needed?
|
55
|
-
uses_slug_cache? && slug? && send(friendly_id_config.cache_column) != slug.to_friendly_id
|
56
|
-
end
|
57
|
-
|
58
|
-
# Reset the cached friendly_id.
|
59
|
-
def set_slug_cache
|
60
|
-
if new_cache_needed?
|
61
|
-
begin
|
62
|
-
send "#{friendly_id_config.cache_column}=", slug.to_friendly_id
|
63
|
-
update_without_callbacks
|
64
|
-
rescue ActiveRecord::StaleObjectError
|
65
|
-
reload
|
66
|
-
retry
|
67
|
-
end
|
68
|
-
end
|
69
|
-
end
|
70
|
-
|
71
|
-
def update_scope
|
72
|
-
return unless slug && scope_changed?
|
73
|
-
self.class.transaction do
|
74
|
-
slug.scope = send(friendly_id_config.scope).to_param
|
75
|
-
similar = Slug.similar_to(slug)
|
76
|
-
if !similar.empty?
|
77
|
-
slug.sequence = similar.first.sequence.succ
|
78
|
-
end
|
79
|
-
slug.save!
|
80
|
-
end
|
81
|
-
end
|
82
|
-
|
83
|
-
# Update the slugs for any model that is using this model as its
|
84
|
-
# FriendlyId scope.
|
85
|
-
def update_dependent_scopes
|
86
|
-
return unless friendly_id_config.class.scopes_used?
|
87
|
-
if slugs(true).size > 1 && @new_friendly_id
|
88
|
-
friendly_id_config.child_scopes.each do |klass|
|
89
|
-
Slug.update_all "scope = '#{@new_friendly_id}'", ["sluggable_type = ? AND scope = ?",
|
90
|
-
klass.to_s, slugs.second.to_friendly_id]
|
91
|
-
end
|
92
|
-
end
|
93
|
-
end
|
94
|
-
|
95
|
-
# Does the model use slug caching?
|
96
|
-
def uses_slug_cache?
|
97
|
-
friendly_id_config.cache_column?
|
98
|
-
end
|
99
|
-
|
100
|
-
# This method was removed in ActiveRecord 3.0.
|
101
|
-
if !ActiveRecord::Base.private_method_defined? :update_without_callbacks
|
102
|
-
def update_without_callbacks
|
103
|
-
attributes_with_values = arel_attributes_values(false, false, attribute_names)
|
104
|
-
return false if attributes_with_values.empty?
|
105
|
-
self.class.unscoped.where(self.class.arel_table[self.class.primary_key].eq(id)).arel.update(attributes_with_values)
|
106
|
-
end
|
107
|
-
end
|
108
|
-
end
|
109
|
-
end
|
110
|
-
end
|
@@ -1,72 +0,0 @@
|
|
1
|
-
module FriendlyId
|
2
|
-
class TaskRunner
|
3
|
-
|
4
|
-
extend Forwardable
|
5
|
-
|
6
|
-
attr_accessor :days
|
7
|
-
attr_accessor :klass
|
8
|
-
attr_accessor :task_options
|
9
|
-
|
10
|
-
def_delegators :klass, :find, :friendly_id_config, :update_all
|
11
|
-
|
12
|
-
OLD_SLUG_DAYS = 45
|
13
|
-
|
14
|
-
def initialize(&block)
|
15
|
-
self.klass = ENV["MODEL"]
|
16
|
-
self.days = ENV["DAYS"]
|
17
|
-
end
|
18
|
-
|
19
|
-
def days=(days)
|
20
|
-
@days ||= days.blank? ? OLD_SLUG_DAYS : days.to_i
|
21
|
-
end
|
22
|
-
|
23
|
-
def klass=(klass)
|
24
|
-
@klass ||= klass.to_s.classify.constantize unless klass.blank?
|
25
|
-
end
|
26
|
-
|
27
|
-
def make_slugs
|
28
|
-
validate_uses_slugs
|
29
|
-
options = {
|
30
|
-
:include => :slug,
|
31
|
-
:limit => (ENV["LIMIT"] || 100).to_i,
|
32
|
-
:offset => 0,
|
33
|
-
:order => ENV["ORDER"] || "#{klass.table_name}.#{klass.primary_key} ASC",
|
34
|
-
}.merge(task_options || {})
|
35
|
-
|
36
|
-
while records = find(:all, options) do
|
37
|
-
break if records.size == 0
|
38
|
-
records.each do |record|
|
39
|
-
record.save(:validate => false) unless record.slug?
|
40
|
-
yield(record) if block_given?
|
41
|
-
end
|
42
|
-
options[:offset] += options[:limit]
|
43
|
-
end
|
44
|
-
end
|
45
|
-
|
46
|
-
def delete_slugs
|
47
|
-
validate_uses_slugs
|
48
|
-
Slug.destroy_all(["sluggable_type = ?", klass.to_s])
|
49
|
-
if column = friendly_id_config.cache_column
|
50
|
-
update_all("#{column} = NULL")
|
51
|
-
end
|
52
|
-
end
|
53
|
-
|
54
|
-
def delete_old_slugs
|
55
|
-
conditions = ["created_at < ?", DateTime.now - days]
|
56
|
-
if klass
|
57
|
-
conditions[0] << " AND sluggable_type = ?"
|
58
|
-
conditions << klass.to_s
|
59
|
-
end
|
60
|
-
Slug.all(:conditions => conditions).select(&:outdated?).map(&:destroy)
|
61
|
-
end
|
62
|
-
|
63
|
-
def validate_uses_slugs
|
64
|
-
(raise "You need to pass a MODEL=<model name> argument to rake") if klass.blank?
|
65
|
-
unless friendly_id_config.use_slug?
|
66
|
-
raise "Class '%s' doesn't use slugs" % klass.to_s
|
67
|
-
end
|
68
|
-
rescue NoMethodError
|
69
|
-
raise "Class '%s' doesn't use FriendlyId" % klass.to_s
|
70
|
-
end
|
71
|
-
end
|
72
|
-
end
|
data/lib/friendly_id/railtie.rb
DELETED
@@ -1,22 +0,0 @@
|
|
1
|
-
module FriendlyId
|
2
|
-
class Railtie < Rails::Railtie
|
3
|
-
|
4
|
-
initializer "friendly_id.configure_rails_initialization" do |app|
|
5
|
-
# Experimental Sequel support. See: http://github.com/norman/friendly_id_sequel
|
6
|
-
if app.config.generators.rails[:orm] == :sequel
|
7
|
-
require "friendly_id/sequel"
|
8
|
-
# Experimental DataMapper support. See: http://github.com/myabc/friendly_id_datamapper
|
9
|
-
elsif app.config.generators.rails[:orm] == :data_mapper
|
10
|
-
require 'friendly_id/datamapper'
|
11
|
-
else
|
12
|
-
# AR is the default.
|
13
|
-
require "friendly_id/active_record"
|
14
|
-
end
|
15
|
-
end
|
16
|
-
|
17
|
-
rake_tasks do
|
18
|
-
load "tasks/friendly_id.rake"
|
19
|
-
end
|
20
|
-
|
21
|
-
end
|
22
|
-
end
|
data/lib/friendly_id/sequel.rb
DELETED
@@ -1,25 +0,0 @@
|
|
1
|
-
# encoding: utf-8
|
2
|
-
module FriendlyId
|
3
|
-
|
4
|
-
class SlugString < Babosa::Identifier
|
5
|
-
# Normalize the string for a given {FriendlyId::Configuration}.
|
6
|
-
# @param config [FriendlyId::Configuration]
|
7
|
-
# @return String
|
8
|
-
def normalize_for!(config)
|
9
|
-
normalize!(config.babosa_options)
|
10
|
-
end
|
11
|
-
|
12
|
-
# Validate that the slug string is not blank or reserved, and truncate
|
13
|
-
# it to the max length if necessary.
|
14
|
-
# @param config [FriendlyId::Configuration]
|
15
|
-
# @return String
|
16
|
-
# @raise FriendlyId::BlankError
|
17
|
-
# @raise FriendlyId::ReservedError
|
18
|
-
def validate_for!(config)
|
19
|
-
truncate_bytes!(config.max_length)
|
20
|
-
raise FriendlyId::BlankError if empty?
|
21
|
-
raise FriendlyId::ReservedError if config.reserved?(self)
|
22
|
-
self
|
23
|
-
end
|
24
|
-
end
|
25
|
-
end
|
data/lib/friendly_id/status.rb
DELETED
@@ -1,35 +0,0 @@
|
|
1
|
-
module FriendlyId
|
2
|
-
|
3
|
-
# FriendlyId::Status presents information about the status of the
|
4
|
-
# id that was used to find the model. This class can be useful for figuring
|
5
|
-
# out when to redirect to a new URL.
|
6
|
-
class Status
|
7
|
-
|
8
|
-
# The id or name used as the finder argument
|
9
|
-
attr_accessor :name
|
10
|
-
|
11
|
-
# The found result, if any
|
12
|
-
attr_accessor :record
|
13
|
-
|
14
|
-
def initialize(options={})
|
15
|
-
options.each {|key, value| self.send("#{key}=".to_sym, value)}
|
16
|
-
end
|
17
|
-
|
18
|
-
# Did the find operation use a friendly id?
|
19
|
-
def friendly?
|
20
|
-
!! name
|
21
|
-
end
|
22
|
-
|
23
|
-
# Did the find operation use a numeric id?
|
24
|
-
def numeric?
|
25
|
-
!friendly?
|
26
|
-
end
|
27
|
-
|
28
|
-
# Did the find operation use the best available id?
|
29
|
-
def best?
|
30
|
-
record.friendly_id ? friendly? : true
|
31
|
-
end
|
32
|
-
|
33
|
-
end
|
34
|
-
|
35
|
-
end
|
data/lib/friendly_id/test.rb
DELETED
@@ -1,364 +0,0 @@
|
|
1
|
-
# encoding: utf-8
|
2
|
-
Module.send :include, Module.new {
|
3
|
-
def test(name, &block)
|
4
|
-
define_method("test_#{name.gsub(/[^a-z0-9]/i, "_")}".to_sym, &block)
|
5
|
-
end
|
6
|
-
alias :should :test
|
7
|
-
}
|
8
|
-
|
9
|
-
module FriendlyId
|
10
|
-
module Test
|
11
|
-
|
12
|
-
# Tests for any model that implements FriendlyId. Any test that tests model
|
13
|
-
# features should include this module.
|
14
|
-
module Generic
|
15
|
-
|
16
|
-
def setup
|
17
|
-
klass.send delete_all_method
|
18
|
-
end
|
19
|
-
|
20
|
-
def teardown
|
21
|
-
klass.send delete_all_method
|
22
|
-
end
|
23
|
-
|
24
|
-
def instance
|
25
|
-
raise NotImplementedError
|
26
|
-
end
|
27
|
-
|
28
|
-
def klass
|
29
|
-
raise NotImplementedError
|
30
|
-
end
|
31
|
-
|
32
|
-
def other_class
|
33
|
-
raise NotImplementedError
|
34
|
-
end
|
35
|
-
|
36
|
-
def find_method
|
37
|
-
raise NotImplementedError
|
38
|
-
end
|
39
|
-
|
40
|
-
def create_method
|
41
|
-
raise NotImplementedError
|
42
|
-
end
|
43
|
-
|
44
|
-
def update_method
|
45
|
-
raise NotImplementedError
|
46
|
-
end
|
47
|
-
|
48
|
-
def validation_exceptions
|
49
|
-
return RuntimeError
|
50
|
-
end
|
51
|
-
|
52
|
-
def assert_validation_error
|
53
|
-
if validation_exceptions
|
54
|
-
assert_raise(*[validation_exceptions].flatten) do
|
55
|
-
yield
|
56
|
-
end
|
57
|
-
else # DataMapper does not raise Validation Errors
|
58
|
-
i = yield
|
59
|
-
if i.kind_of?(TrueClass) || i.kind_of?(FalseClass)
|
60
|
-
assert !i
|
61
|
-
else
|
62
|
-
instance = i
|
63
|
-
assert !instance.errors.empty?
|
64
|
-
end
|
65
|
-
end
|
66
|
-
end
|
67
|
-
|
68
|
-
test "models should have a friendly id config" do
|
69
|
-
assert_not_nil klass.friendly_id_config
|
70
|
-
end
|
71
|
-
|
72
|
-
test "instances should have a friendly id by default" do
|
73
|
-
assert_not_nil instance.friendly_id
|
74
|
-
end
|
75
|
-
|
76
|
-
test "instances should have a friendly id status" do
|
77
|
-
assert_not_nil instance.friendly_id_status
|
78
|
-
end
|
79
|
-
|
80
|
-
test "instances should be findable by their friendly id" do
|
81
|
-
assert_equal instance, klass.send(find_method, instance.friendly_id)
|
82
|
-
end
|
83
|
-
|
84
|
-
test "instances should be findable by their numeric id as an integer" do
|
85
|
-
assert_equal instance, klass.send(find_method, instance.id.to_i)
|
86
|
-
end
|
87
|
-
|
88
|
-
test "instances should be findable by their numeric id as a string" do
|
89
|
-
assert_equal instance, klass.send(find_method, instance.id.to_s)
|
90
|
-
end
|
91
|
-
|
92
|
-
test "instances should be findable by a numeric friendly_id" do
|
93
|
-
instance = klass.send(create_method, :name => "206")
|
94
|
-
assert_equal instance, klass.send(find_method, "206")
|
95
|
-
end
|
96
|
-
|
97
|
-
test "creation should raise an error if the friendly_id text is reserved" do
|
98
|
-
assert_validation_error do
|
99
|
-
klass.send(create_method, :name => "new")
|
100
|
-
end
|
101
|
-
end
|
102
|
-
|
103
|
-
test "creation should raise an error if the friendly_id text is an empty string" do
|
104
|
-
assert_validation_error do
|
105
|
-
klass.send(create_method, :name => "")
|
106
|
-
end
|
107
|
-
end
|
108
|
-
|
109
|
-
test "creation should raise an error if the friendly_id text is a blank string" do
|
110
|
-
assert_validation_error do
|
111
|
-
klass.send(create_method, :name => " ")
|
112
|
-
end
|
113
|
-
end
|
114
|
-
|
115
|
-
test "creation should raise an error if the friendly_id text is nil and allow_nil is false" do
|
116
|
-
assert_validation_error do
|
117
|
-
klass.send(create_method, :name => nil)
|
118
|
-
end
|
119
|
-
end
|
120
|
-
|
121
|
-
test "creation should succeed if the friendly_id text is nil and allow_nil is true" do
|
122
|
-
klass.friendly_id_config.stubs(:allow_nil?).returns(true)
|
123
|
-
assert klass.send(create_method, :name => nil)
|
124
|
-
end
|
125
|
-
|
126
|
-
test "creation should succeed if the friendly_id text is empty and allow_nil is true" do
|
127
|
-
klass.friendly_id_config.stubs(:allow_nil?).returns(true)
|
128
|
-
begin
|
129
|
-
assert klass.send(create_method, :name => "")
|
130
|
-
rescue ActiveRecord::RecordInvalid => e
|
131
|
-
raise unless e.message =~ /Name can't be blank/ # Test not applicable in this case
|
132
|
-
end
|
133
|
-
end
|
134
|
-
|
135
|
-
test "creation should succeed if the friendly_id text converts to empty and allow_nil is true" do
|
136
|
-
klass.friendly_id_config.stubs(:allow_nil?).returns(true)
|
137
|
-
assert klass.send(create_method, :name => "*")
|
138
|
-
end
|
139
|
-
|
140
|
-
test "should allow the same friendly_id across models" do
|
141
|
-
other_instance = other_class.send(create_method, :name => instance.name)
|
142
|
-
assert_equal other_instance.friendly_id, instance.friendly_id
|
143
|
-
end
|
144
|
-
|
145
|
-
test "reserved words can be specified as a regular expression" do
|
146
|
-
klass.friendly_id_config.stubs(:reserved_words).returns(/jo/)
|
147
|
-
assert_validation_error do
|
148
|
-
klass.send(create_method, :name => "joe")
|
149
|
-
end
|
150
|
-
end
|
151
|
-
|
152
|
-
test "should not raise reserved error unless regexp matches" do
|
153
|
-
klass.friendly_id_config.stubs(:reserved_words).returns(/ddsadad/)
|
154
|
-
assert_nothing_raised do
|
155
|
-
klass.send(create_method, :name => "joe")
|
156
|
-
end
|
157
|
-
end
|
158
|
-
|
159
|
-
end
|
160
|
-
|
161
|
-
module Simple
|
162
|
-
|
163
|
-
test "should allow friendly_id to be nillable if allow_nil is true" do
|
164
|
-
klass.friendly_id_config.stubs(:allow_nil?).returns(true)
|
165
|
-
instance = klass.send(create_method, :name => "hello")
|
166
|
-
assert instance.friendly_id
|
167
|
-
instance.name = nil
|
168
|
-
assert instance.send(save_method)
|
169
|
-
end
|
170
|
-
|
171
|
-
end
|
172
|
-
|
173
|
-
# Tests for any model that implements slugs.
|
174
|
-
module Slugged
|
175
|
-
|
176
|
-
test "should have a slug" do
|
177
|
-
assert_not_nil instance.slug
|
178
|
-
end
|
179
|
-
|
180
|
-
test "should not make a new slug unless the friendly_id method value has changed" do
|
181
|
-
instance.note = instance.note.to_s << " updated"
|
182
|
-
instance.send save_method
|
183
|
-
assert_equal 1, instance.slugs.size
|
184
|
-
end
|
185
|
-
|
186
|
-
test "should make a new slug if the friendly_id method value has changed" do
|
187
|
-
instance.name = "Changed title"
|
188
|
-
instance.send save_method
|
189
|
-
slugs = if instance.slugs.respond_to?(:reload)
|
190
|
-
instance.slugs.reload
|
191
|
-
else
|
192
|
-
instance.slugs(true)
|
193
|
-
end
|
194
|
-
assert_equal 2, slugs.size
|
195
|
-
end
|
196
|
-
|
197
|
-
test "should be able to reuse an old friendly_id without incrementing the sequence" do
|
198
|
-
old_title = instance.name
|
199
|
-
old_friendly_id = instance.friendly_id
|
200
|
-
instance.name = "A changed title"
|
201
|
-
instance.send save_method
|
202
|
-
instance.name = old_title
|
203
|
-
instance.send save_method
|
204
|
-
assert_equal old_friendly_id, instance.friendly_id
|
205
|
-
end
|
206
|
-
|
207
|
-
test "should increment the slug sequence for duplicate friendly ids" do
|
208
|
-
instance2 = klass.send(create_method, :name => instance.name)
|
209
|
-
assert_match(/2\z/, instance2.friendly_id)
|
210
|
-
end
|
211
|
-
|
212
|
-
test "should find instance with a sequenced friendly_id" do
|
213
|
-
instance2 = klass.send(create_method, :name => instance.name)
|
214
|
-
assert_equal instance2, klass.send(find_method, instance2.friendly_id)
|
215
|
-
end
|
216
|
-
|
217
|
-
test "should indicate correct status when found with a sequence" do
|
218
|
-
instance2 = klass.send(create_method, :name => instance.name)
|
219
|
-
instance2 = klass.send(find_method, instance2.friendly_id)
|
220
|
-
assert instance2.friendly_id_status.best?
|
221
|
-
end
|
222
|
-
|
223
|
-
test "should indicate correct status when found by a numeric friendly_id" do
|
224
|
-
instance = klass.send(create_method, :name => "100")
|
225
|
-
instance2 = klass.send(find_method, "100")
|
226
|
-
assert instance2.friendly_id_status.best?, "status expected to be best but isn't."
|
227
|
-
assert instance2.friendly_id_status.current?, "status expected to be current but isn't."
|
228
|
-
end
|
229
|
-
|
230
|
-
test "should remain findable by previous slugs" do
|
231
|
-
old_friendly_id = instance.friendly_id
|
232
|
-
instance.name = "#{old_friendly_id} updated"
|
233
|
-
instance.send(save_method)
|
234
|
-
assert_not_equal old_friendly_id, instance.friendly_id
|
235
|
-
assert_equal instance, klass.send(find_method, old_friendly_id)
|
236
|
-
end
|
237
|
-
|
238
|
-
test "should not create a slug when allow_nil is true and friendy_id text is blank" do
|
239
|
-
klass.friendly_id_config.stubs(:allow_nil?).returns(true)
|
240
|
-
instance = klass.send(create_method, :name => nil)
|
241
|
-
assert_nil instance.slug
|
242
|
-
end
|
243
|
-
|
244
|
-
test "should not allow friendly_id to be nillable even if allow_nil is true" do
|
245
|
-
klass.friendly_id_config.stubs(:allow_nil?).returns(true)
|
246
|
-
instance = klass.send(create_method, :name => "hello")
|
247
|
-
assert instance.friendly_id
|
248
|
-
instance.name = nil
|
249
|
-
assert_validation_error do
|
250
|
-
instance.send(save_method)
|
251
|
-
end
|
252
|
-
end
|
253
|
-
|
254
|
-
test "should approximate ascii if configured" do
|
255
|
-
klass.friendly_id_config.stubs(:approximate_ascii?).returns(true)
|
256
|
-
instance = klass.send(create_method, :name => "Cañón")
|
257
|
-
assert_equal "canon", instance.friendly_id
|
258
|
-
end
|
259
|
-
|
260
|
-
test "should approximate ascii with options if configured" do
|
261
|
-
klass.friendly_id_config.stubs(:approximate_ascii?).returns(true)
|
262
|
-
klass.friendly_id_config.stubs(:ascii_approximation_options).returns(:spanish)
|
263
|
-
instance = klass.send(create_method, :name => "Cañón")
|
264
|
-
assert_equal "canion", instance.friendly_id
|
265
|
-
end
|
266
|
-
end
|
267
|
-
|
268
|
-
# Tests for FriendlyId::Status.
|
269
|
-
module Status
|
270
|
-
|
271
|
-
test "should default to not friendly" do
|
272
|
-
assert !status.friendly?
|
273
|
-
end
|
274
|
-
|
275
|
-
test "should default to numeric" do
|
276
|
-
assert status.numeric?
|
277
|
-
end
|
278
|
-
|
279
|
-
end
|
280
|
-
|
281
|
-
# Tests for FriendlyId::Status for a model that uses slugs.
|
282
|
-
module SluggedStatus
|
283
|
-
|
284
|
-
test "should be friendly if slug is set" do
|
285
|
-
status.slug = Slug.new
|
286
|
-
assert status.friendly?
|
287
|
-
end
|
288
|
-
|
289
|
-
test "should be friendly if name is set" do
|
290
|
-
status.name = "name"
|
291
|
-
assert status.friendly?
|
292
|
-
end
|
293
|
-
|
294
|
-
test "should be current if current slug is set" do
|
295
|
-
status.slug = instance.slug
|
296
|
-
assert status.current?
|
297
|
-
end
|
298
|
-
|
299
|
-
test "should not be current if non-current slug is set" do
|
300
|
-
status.slug = Slug.new(:sluggable => instance)
|
301
|
-
assert !status.current?
|
302
|
-
end
|
303
|
-
|
304
|
-
test "should be best if it is current" do
|
305
|
-
status.slug = instance.slug
|
306
|
-
assert status.best?
|
307
|
-
end
|
308
|
-
|
309
|
-
test "should be best if it is numeric, but record has no slug" do
|
310
|
-
instance.slugs = []
|
311
|
-
instance.slug = nil
|
312
|
-
assert status.best?
|
313
|
-
end
|
314
|
-
|
315
|
-
[:record, :name].each do |symbol|
|
316
|
-
test "should have #{symbol} after find using friendly_id" do
|
317
|
-
instance2 = klass.send(find_method, instance.friendly_id)
|
318
|
-
assert_not_nil instance2.friendly_id_status.send(symbol)
|
319
|
-
end
|
320
|
-
end
|
321
|
-
|
322
|
-
def status
|
323
|
-
@status ||= instance.friendly_id_status
|
324
|
-
end
|
325
|
-
|
326
|
-
def klass
|
327
|
-
raise NotImplementedError
|
328
|
-
end
|
329
|
-
|
330
|
-
def instance
|
331
|
-
raise NotImplementedError
|
332
|
-
end
|
333
|
-
|
334
|
-
end
|
335
|
-
|
336
|
-
# Tests for models to ensure that they properly implement using the
|
337
|
-
# +normalize_friendly_id+ method to allow developers to hook into the
|
338
|
-
# slug string generation.
|
339
|
-
module CustomNormalizer
|
340
|
-
|
341
|
-
test "should invoke the custom normalizer" do
|
342
|
-
assert_equal "JOE SCHMOE", klass.send(create_method, :name => "Joe Schmoe").friendly_id
|
343
|
-
end
|
344
|
-
|
345
|
-
test "should respect the max_length option" do
|
346
|
-
klass.friendly_id_config.stubs(:max_length).returns(3)
|
347
|
-
assert_equal "JOE", klass.send(create_method, :name => "Joe Schmoe").friendly_id
|
348
|
-
end
|
349
|
-
|
350
|
-
test "should raise an error if the friendly_id text is reserved" do
|
351
|
-
klass.friendly_id_config.stubs(:reserved_words).returns(["JOE"])
|
352
|
-
if validation_exceptions
|
353
|
-
assert_raise(*[validation_exceptions].flatten) do
|
354
|
-
klass.send(create_method, :name => "Joe")
|
355
|
-
end
|
356
|
-
else # DataMapper does not raise Validation Errors
|
357
|
-
instance = klass.send(create_method, :name => "Joe")
|
358
|
-
assert !instance.errors.empty?
|
359
|
-
end
|
360
|
-
end
|
361
|
-
|
362
|
-
end
|
363
|
-
end
|
364
|
-
end
|