friendly_id 4.0.0.beta7 → 4.0.0.beta8
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.
- 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
|