isa-friendly_id_datamapper 3.2.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.
- data/ChangeLog.md +8 -0
- data/MIT-LICENSE +19 -0
- data/README.md +63 -0
- data/Rakefile +29 -0
- data/lib/friendly_id/datamapper_adapter/configuration.rb +67 -0
- data/lib/friendly_id/datamapper_adapter/simple_model.rb +83 -0
- data/lib/friendly_id/datamapper_adapter/slug.rb +72 -0
- data/lib/friendly_id/datamapper_adapter/slugged_model.rb +186 -0
- data/lib/friendly_id/datamapper_adapter/tasks.rb +69 -0
- data/lib/friendly_id/datamapper_adapter/version.rb +11 -0
- data/lib/friendly_id_datamapper.rb +34 -0
- data/test/basic_slugged_model_test.rb +16 -0
- data/test/cached_slug_test.rb +74 -0
- data/test/core.rb +72 -0
- data/test/custom_normalizer_test.rb +20 -0
- data/test/custom_table_name_test.rb +22 -0
- data/test/scoped_model_test.rb +120 -0
- data/test/simple_test.rb +76 -0
- data/test/slug_test.rb +35 -0
- data/test/slugged.rb +24 -0
- data/test/slugged_status_test.rb +27 -0
- data/test/sti_test.rb +22 -0
- data/test/support/models.rb +185 -0
- data/test/tasks_test.rb +84 -0
- data/test/test_helper.rb +37 -0
- metadata +158 -0
data/ChangeLog.md
ADDED
data/MIT-LICENSE
ADDED
@@ -0,0 +1,19 @@
|
|
1
|
+
Copyright (c) 2010 Norman Clarke, Alex Coles
|
2
|
+
|
3
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
4
|
+
of this software and associated documentation files (the "Software"), to deal
|
5
|
+
in the Software without restriction, including without limitation the rights
|
6
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
7
|
+
copies of the Software, and to permit persons to whom the Software is
|
8
|
+
furnished to do so, subject to the following conditions:
|
9
|
+
|
10
|
+
The above copyright notice and this permission notice shall be included in all
|
11
|
+
copies or substantial portions of the Software.
|
12
|
+
|
13
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
14
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
15
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
16
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
17
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
18
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
19
|
+
SOFTWARE.
|
data/README.md
ADDED
@@ -0,0 +1,63 @@
|
|
1
|
+
# FriendlyId DataMapper Adapter
|
2
|
+
|
3
|
+
This is an pre-release (beta) adapter for
|
4
|
+
[FriendlyId](http://norman.github.com/friendly_id) using DataMapper.
|
5
|
+
|
6
|
+
## FriendlyId Features
|
7
|
+
|
8
|
+
It currently supports all of FriendlyId's features except:
|
9
|
+
|
10
|
+
* Rails Generator
|
11
|
+
* Support for multiple finders
|
12
|
+
|
13
|
+
Currently, only finds using `get` is supported.
|
14
|
+
|
15
|
+
@post = Post.get("this-is-a-title")
|
16
|
+
@post.friendly_id # this-is-a-title
|
17
|
+
|
18
|
+
## Compatibility
|
19
|
+
|
20
|
+
The FriendlyId DataMapper Adapter keeps in lock-step with major and
|
21
|
+
minor versions of the FriendlyId gem, i.e.
|
22
|
+
`friendly_id_datamapper 3.1.x` is compatible with `friendly_id 3.1.x series`.
|
23
|
+
Patch and build versions are not kept in lock-step.
|
24
|
+
|
25
|
+
## Usage
|
26
|
+
|
27
|
+
gem install friendly_id friendly_id_datamapper
|
28
|
+
|
29
|
+
require "friendly_id"
|
30
|
+
require "friendly_id/datamapper"
|
31
|
+
|
32
|
+
class Post
|
33
|
+
include DataMapper::Resource
|
34
|
+
|
35
|
+
property :id, Serial
|
36
|
+
property :title, String
|
37
|
+
|
38
|
+
has_friendly_id :title, :use_slug => true
|
39
|
+
end
|
40
|
+
|
41
|
+
|
42
|
+
For more information on the available features, please see the
|
43
|
+
[FriendlyId Guide](http://norman.github.com/friendly_id/file.Guide.html).
|
44
|
+
|
45
|
+
## Bugs
|
46
|
+
|
47
|
+
Please report them on the [Github issue tracker](http://github.com/myabc/friendly_id_datamapper/issues)
|
48
|
+
for this project.
|
49
|
+
|
50
|
+
If you have a bug to report, please include the following information:
|
51
|
+
|
52
|
+
* **Version information for FriendlyId, friendly_id_datamapper, Rails and Ruby.**
|
53
|
+
* Stack trace and error message.
|
54
|
+
* Any snippets of relevant model, view or controller code that shows how your
|
55
|
+
are using FriendlyId.
|
56
|
+
|
57
|
+
If you are able to, it helps even more if you can fork FriendlyId on Github,
|
58
|
+
and add a test that reproduces the error you are experiencing.
|
59
|
+
|
60
|
+
## Credits
|
61
|
+
|
62
|
+
Copyright (c) 2010, released under the MIT license.
|
63
|
+
|
data/Rakefile
ADDED
@@ -0,0 +1,29 @@
|
|
1
|
+
require "rake"
|
2
|
+
require "rake/testtask"
|
3
|
+
require "rake/gempackagetask"
|
4
|
+
require "rake/clean"
|
5
|
+
|
6
|
+
CLEAN << "pkg" << "doc" << "coverage" << ".yardoc"
|
7
|
+
|
8
|
+
Rake::GemPackageTask.new(eval(File.read("friendly_id_datamapper.gemspec"))) { |pkg| }
|
9
|
+
Rake::TestTask.new(:test) { |t| t.pattern = "test/*_test.rb" }
|
10
|
+
|
11
|
+
task :default => :test
|
12
|
+
|
13
|
+
begin
|
14
|
+
require "yard"
|
15
|
+
YARD::Rake::YardocTask.new do |t|
|
16
|
+
t.options = ["--output-dir=docs"]
|
17
|
+
end
|
18
|
+
rescue LoadError
|
19
|
+
end
|
20
|
+
|
21
|
+
begin
|
22
|
+
require "rcov/rcovtask"
|
23
|
+
Rcov::RcovTask.new do |r|
|
24
|
+
r.test_files = FileList["test/**/*_test.rb"]
|
25
|
+
r.verbose = true
|
26
|
+
r.rcov_opts << "--exclude gems/*"
|
27
|
+
end
|
28
|
+
rescue LoadError
|
29
|
+
end
|
@@ -0,0 +1,67 @@
|
|
1
|
+
module FriendlyId
|
2
|
+
module DataMapperAdapter
|
3
|
+
|
4
|
+
# Extends FriendlyId::Configuration with some implementation details and
|
5
|
+
# features specific to DataMapper.
|
6
|
+
class Configuration < FriendlyId::Configuration
|
7
|
+
|
8
|
+
# The column used to cache the friendly_id string. If no column is specified,
|
9
|
+
# FriendlyId will look for a column named +cached_slug+ and use it automatically
|
10
|
+
# if it exists. If for some reason you have a column named +cached_slug+
|
11
|
+
# but don't want FriendlyId to modify it, pass the option
|
12
|
+
# +:cache_column => false+ to {FriendlyId::DataMapperAdapter#has_friendly_id has_friendly_id}.
|
13
|
+
attr_accessor :cache_column
|
14
|
+
|
15
|
+
# An array of classes for which the configured class serves as a
|
16
|
+
# FriendlyId scope.
|
17
|
+
attr_reader :child_scopes
|
18
|
+
|
19
|
+
attr_reader :custom_cache_column
|
20
|
+
|
21
|
+
def cache_column
|
22
|
+
return @cache_column if defined?(@cache_column)
|
23
|
+
@cache_column = autodiscover_cache_column
|
24
|
+
end
|
25
|
+
|
26
|
+
def cache_column?
|
27
|
+
!! cache_column
|
28
|
+
end
|
29
|
+
|
30
|
+
def cache_column=(cache_column)
|
31
|
+
@cache_column = cache_column
|
32
|
+
@custom_cache_column = cache_column
|
33
|
+
end
|
34
|
+
|
35
|
+
def child_scopes
|
36
|
+
@child_scopes ||= associated_friendly_classes.select do |klass|
|
37
|
+
klass.friendly_id_config.scopes_over?(configured_class)
|
38
|
+
end
|
39
|
+
end
|
40
|
+
|
41
|
+
def custom_cache_column?
|
42
|
+
!! custom_cache_column
|
43
|
+
end
|
44
|
+
|
45
|
+
def scope_for(record)
|
46
|
+
scope? ? record.send(scope).to_param : nil
|
47
|
+
end
|
48
|
+
|
49
|
+
def scopes_over?(klass)
|
50
|
+
scope? && scope == klass.to_s.underscore.to_sym
|
51
|
+
end
|
52
|
+
|
53
|
+
private
|
54
|
+
|
55
|
+
def autodiscover_cache_column
|
56
|
+
:cached_slug if configured_class.properties[:cached_slug]
|
57
|
+
end
|
58
|
+
|
59
|
+
def associated_friendly_classes
|
60
|
+
configured_class.relationships.values.select { |relationship|
|
61
|
+
relationship.child_model.respond_to?(:friendly_id_config)
|
62
|
+
}.map(&:child_model)
|
63
|
+
end
|
64
|
+
|
65
|
+
end
|
66
|
+
end
|
67
|
+
end
|
@@ -0,0 +1,83 @@
|
|
1
|
+
|
2
|
+
|
3
|
+
module FriendlyId
|
4
|
+
module DataMapperAdapter
|
5
|
+
|
6
|
+
module SimpleModel
|
7
|
+
|
8
|
+
def self.included(base)
|
9
|
+
base.class_eval do
|
10
|
+
column = friendly_id_config.column
|
11
|
+
validates_presence_of column, :unless => :skip_friendly_id_validations
|
12
|
+
validates_length_of column, :maximum => friendly_id_config.max_length, :unless => :skip_friendly_id_validations
|
13
|
+
validates_with_method column, :method => :validate_friendly_id, :unless => :skip_friendly_id_validations
|
14
|
+
|
15
|
+
before :update do
|
16
|
+
@old_friendly_id = original_attributes[properties[friendly_id_config.column]]
|
17
|
+
end
|
18
|
+
|
19
|
+
after :update, :update_scopes
|
20
|
+
end
|
21
|
+
|
22
|
+
def base.get(*key)
|
23
|
+
if key.size == 1
|
24
|
+
return super if key.first.unfriendly_id?
|
25
|
+
column = self.friendly_id_config.column
|
26
|
+
repository = self.repository
|
27
|
+
key = self.key(repository.name).typecast(key)
|
28
|
+
result = self.first(column.to_sym => key)
|
29
|
+
return super unless result
|
30
|
+
result.friendly_id_status.name = name
|
31
|
+
result
|
32
|
+
else
|
33
|
+
super
|
34
|
+
end
|
35
|
+
end
|
36
|
+
end
|
37
|
+
|
38
|
+
# Get the {FriendlyId::Status} after the find has been performed.
|
39
|
+
def friendly_id_status
|
40
|
+
@friendly_id_status ||= Status.new(:record => self)
|
41
|
+
end
|
42
|
+
|
43
|
+
# Returns the friendly_id.
|
44
|
+
def friendly_id
|
45
|
+
send friendly_id_config.column
|
46
|
+
end
|
47
|
+
|
48
|
+
# Returns the friendly id, or if none is available, the numeric id.
|
49
|
+
def to_param
|
50
|
+
(friendly_id || id).to_s
|
51
|
+
end
|
52
|
+
|
53
|
+
private
|
54
|
+
|
55
|
+
# Update the slugs for any model that is using this model as its
|
56
|
+
# FriendlyId scope.
|
57
|
+
def update_scopes
|
58
|
+
if @old_friendly_id != friendly_id
|
59
|
+
friendly_id_config.child_scopes.each do |klass|
|
60
|
+
Slug.all(:sluggable_type => klass, :scope => @old_friendly_id).update(:scope => friendly_id)
|
61
|
+
end
|
62
|
+
end
|
63
|
+
end
|
64
|
+
|
65
|
+
def friendly_id_config
|
66
|
+
self.class.friendly_id_config
|
67
|
+
end
|
68
|
+
|
69
|
+
def skip_friendly_id_validations
|
70
|
+
friendly_id.nil? && friendly_id_config.allow_nil?
|
71
|
+
end
|
72
|
+
|
73
|
+
def validate_friendly_id
|
74
|
+
if result = friendly_id_config.reserved_error_message(friendly_id)
|
75
|
+
return [false, result.join(' ')]
|
76
|
+
else
|
77
|
+
return true
|
78
|
+
end
|
79
|
+
end
|
80
|
+
|
81
|
+
end
|
82
|
+
end
|
83
|
+
end
|
@@ -0,0 +1,72 @@
|
|
1
|
+
# A Slug is a unique, human-friendly identifier for a DataMapper model
|
2
|
+
class Slug
|
3
|
+
include ::DataMapper::Resource
|
4
|
+
|
5
|
+
property :id, Serial
|
6
|
+
property :name, String, :index => :index_slugs_on_n_s_s_and_s, :required => true, :length => 1..255
|
7
|
+
property :sluggable_id, Integer, :index => :sluggable_id
|
8
|
+
property :sequence, Integer, :index => :index_slugs_on_n_s_s_and_s, :required => true, :default => 1
|
9
|
+
property :sluggable_type, Class, :index => :index_slugs_on_n_s_s_and_s
|
10
|
+
property :scope, String, :index => :index_slugs_on_n_s_s_and_s
|
11
|
+
property :created_at, DateTime
|
12
|
+
|
13
|
+
before :save do
|
14
|
+
self.sequence = next_sequence
|
15
|
+
self.created_at = DateTime.now
|
16
|
+
end
|
17
|
+
|
18
|
+
def self.similar_to(slug)
|
19
|
+
all({
|
20
|
+
:name => slug.name,
|
21
|
+
:scope => slug.scope,
|
22
|
+
:sluggable_type => slug.sluggable_type,
|
23
|
+
:order => [:sequence.asc]
|
24
|
+
})
|
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
|
+
def sluggable
|
41
|
+
sluggable_type.get(sluggable_id)
|
42
|
+
end
|
43
|
+
|
44
|
+
def sluggable=(instance)
|
45
|
+
attribute_set(:sluggable_type, instance.class)
|
46
|
+
attribute_set(:sluggable_id, instance.id)
|
47
|
+
end
|
48
|
+
|
49
|
+
private
|
50
|
+
|
51
|
+
def enable_name_reversion
|
52
|
+
conditions = { :sluggable_id => sluggable_id, :sluggable_type => sluggable_type,
|
53
|
+
:name => name, :scope => scope }
|
54
|
+
self.class.all(conditions).destroy
|
55
|
+
end
|
56
|
+
|
57
|
+
def friendly_id_with_sequence
|
58
|
+
"#{name}#{separator}#{sequence}"
|
59
|
+
end
|
60
|
+
|
61
|
+
def next_sequence
|
62
|
+
enable_name_reversion
|
63
|
+
conditions = { :name => name, :scope => scope, :sluggable_type => sluggable_type }
|
64
|
+
prev = self.class.first(conditions.update(:order => :sequence.desc))
|
65
|
+
prev ? prev.sequence.succ : 1
|
66
|
+
end
|
67
|
+
|
68
|
+
def separator
|
69
|
+
sluggable_type.friendly_id_config.sequence_separator
|
70
|
+
end
|
71
|
+
|
72
|
+
end
|
@@ -0,0 +1,186 @@
|
|
1
|
+
require 'friendly_id/slugged'
|
2
|
+
require 'friendly_id/status'
|
3
|
+
|
4
|
+
module FriendlyId
|
5
|
+
module DataMapperAdapter
|
6
|
+
|
7
|
+
module SluggedModel
|
8
|
+
|
9
|
+
include FriendlyId::Slugged::Model
|
10
|
+
|
11
|
+
def self.included(base)
|
12
|
+
base.class_eval do
|
13
|
+
has n, :slugs,
|
14
|
+
:model => ::Slug,
|
15
|
+
:child_key => [:sluggable_id],
|
16
|
+
:conditions => { :sluggable_type => base },
|
17
|
+
:order => [:id.desc]
|
18
|
+
|
19
|
+
before :save do
|
20
|
+
begin
|
21
|
+
build_slug if new_slug_needed?
|
22
|
+
method = friendly_id_config.method
|
23
|
+
rescue FriendlyId::BlankError
|
24
|
+
@errors ||= ValidationErrors.new
|
25
|
+
@errors[method] = "can't be blank"
|
26
|
+
throw :halt, false
|
27
|
+
rescue FriendlyId::ReservedError
|
28
|
+
@errors ||= ValidationErrors.new
|
29
|
+
@errors[method] = "is reserved"
|
30
|
+
throw :halt, false
|
31
|
+
end
|
32
|
+
end
|
33
|
+
|
34
|
+
after(:save) do
|
35
|
+
throw :halt, false if friendly_id_config.allow_nil? && !slug
|
36
|
+
|
37
|
+
slug.sluggable_id = id
|
38
|
+
slug.save
|
39
|
+
set_slug_cache
|
40
|
+
end
|
41
|
+
|
42
|
+
after :update, :update_scope
|
43
|
+
after :update, :update_dependent_scopes
|
44
|
+
|
45
|
+
before(:destroy) do
|
46
|
+
slugs.destroy!
|
47
|
+
end
|
48
|
+
end
|
49
|
+
|
50
|
+
base.class_eval <<-RUBY, __FILE__, __LINE__ + 1
|
51
|
+
def self.get(*key)
|
52
|
+
options = key.extract_options!
|
53
|
+
|
54
|
+
if key.size == 1
|
55
|
+
return super if key.first.is_a?(Array) || key.first.unfriendly_id?
|
56
|
+
name, sequence = key.first.to_s.parse_friendly_id
|
57
|
+
|
58
|
+
if !friendly_id_config.scope? && friendly_id_config.cache_column?
|
59
|
+
result = self.first(friendly_id_config.cache_column => key.first)
|
60
|
+
end
|
61
|
+
|
62
|
+
conditions = {
|
63
|
+
slugs.name => name,
|
64
|
+
slugs.sequence => sequence
|
65
|
+
}
|
66
|
+
conditions.merge!({
|
67
|
+
slugs.scope => (options[:scope].to_param if options[:scope] && options[:scope].respond_to?(:to_param))
|
68
|
+
}) if friendly_id_config.scope?
|
69
|
+
|
70
|
+
result ||= self.first(conditions)
|
71
|
+
return super unless result
|
72
|
+
result.friendly_id_status.name = name
|
73
|
+
result.friendly_id_status.sequence = sequence
|
74
|
+
result
|
75
|
+
else
|
76
|
+
super
|
77
|
+
end
|
78
|
+
end
|
79
|
+
|
80
|
+
def self.get!(*key)
|
81
|
+
return super unless friendly_id_config.scope?
|
82
|
+
|
83
|
+
result = get(*key)
|
84
|
+
if result
|
85
|
+
result
|
86
|
+
else
|
87
|
+
options = key.extract_options!
|
88
|
+
scope = options[:scope]
|
89
|
+
message = "Could not find \#{self.name} with key \#{key.inspect}"
|
90
|
+
message << " and scope \#{scope.inspect}" if scope
|
91
|
+
message << ". Scope expected but none given." unless scope
|
92
|
+
raise(::DataMapper::ObjectNotFoundError, message)
|
93
|
+
end
|
94
|
+
end
|
95
|
+
RUBY
|
96
|
+
end
|
97
|
+
|
98
|
+
def slug
|
99
|
+
@slug ||= slugs.first
|
100
|
+
end
|
101
|
+
|
102
|
+
def find_slug(name, sequence)
|
103
|
+
@slug = slugs.first(:name => name, :sequence => sequence)
|
104
|
+
end
|
105
|
+
|
106
|
+
# Returns the friendly id, or if none is available, the numeric id. Note that this
|
107
|
+
# method will use the cached_slug value if present, unlike {#friendly_id}.
|
108
|
+
def to_param
|
109
|
+
friendly_id_config.cache_column ? to_param_from_cache : to_param_from_slug
|
110
|
+
end
|
111
|
+
|
112
|
+
private
|
113
|
+
|
114
|
+
def scope_changed?
|
115
|
+
friendly_id_config.scope? && send(friendly_id_config.scope).to_param != slug.scope
|
116
|
+
end
|
117
|
+
|
118
|
+
# Respond with the cached value if available.
|
119
|
+
def to_param_from_cache
|
120
|
+
attribute_get(friendly_id_config.cache_column) || id.to_s
|
121
|
+
end
|
122
|
+
|
123
|
+
# Respond with the slugged value if available.
|
124
|
+
def to_param_from_slug
|
125
|
+
slug? ? slug.to_friendly_id : id.to_s
|
126
|
+
end
|
127
|
+
|
128
|
+
# Build the new slug using the generated friendly id.
|
129
|
+
def build_slug
|
130
|
+
return unless new_slug_needed?
|
131
|
+
@slug = slugs.new(:name => slug_text, :scope => friendly_id_config.scope_for(self))
|
132
|
+
raise FriendlyId::BlankError unless @slug.valid?
|
133
|
+
@new_friendly_id = @slug.to_friendly_id
|
134
|
+
@slug
|
135
|
+
end
|
136
|
+
|
137
|
+
# Reset the cached friendly_id?
|
138
|
+
def new_cache_needed?
|
139
|
+
uses_slug_cache? && slug? && send(friendly_id_config.cache_column) != slug.to_friendly_id
|
140
|
+
end
|
141
|
+
|
142
|
+
# Reset the cached friendly_id.
|
143
|
+
def set_slug_cache
|
144
|
+
if new_cache_needed?
|
145
|
+
self.attribute_set(friendly_id_config.cache_column, slug.to_friendly_id)
|
146
|
+
self.save_self(false) # save!
|
147
|
+
end
|
148
|
+
end
|
149
|
+
|
150
|
+
def skip_friendly_id_validations
|
151
|
+
friendly_id.nil? && friendly_id_config.allow_nil?
|
152
|
+
end
|
153
|
+
|
154
|
+
def update_scope
|
155
|
+
return unless slug && scope_changed?
|
156
|
+
transaction do
|
157
|
+
slug.scope = send(friendly_id_config.scope).to_param
|
158
|
+
similar = Slug.similar_to(slug)
|
159
|
+
if !similar.empty?
|
160
|
+
slug.sequence = similar.first.sequence.succ
|
161
|
+
end
|
162
|
+
slug.save
|
163
|
+
end
|
164
|
+
end
|
165
|
+
|
166
|
+
# Update the slugs for any model that is using this model as its
|
167
|
+
# FriendlyId scope.
|
168
|
+
def update_dependent_scopes
|
169
|
+
return unless friendly_id_config.class.scopes_used?
|
170
|
+
# slugs.reload.size == 1, slugs.dirty? == true
|
171
|
+
if slugs.size > 1 && @new_friendly_id
|
172
|
+
friendly_id_config.child_scopes.each do |klass|
|
173
|
+
# slugs.first -- ordering not respected when dirty
|
174
|
+
Slug.all(:sluggable_type => klass, :scope => slugs.first.to_friendly_id).update(:scope => @new_friendly_id)
|
175
|
+
end
|
176
|
+
end
|
177
|
+
end
|
178
|
+
|
179
|
+
# Does the model use slug caching?
|
180
|
+
def uses_slug_cache?
|
181
|
+
friendly_id_config.cache_column?
|
182
|
+
end
|
183
|
+
|
184
|
+
end
|
185
|
+
end
|
186
|
+
end
|
@@ -0,0 +1,69 @@
|
|
1
|
+
require 'active_support/core_ext'
|
2
|
+
|
3
|
+
module FriendlyId
|
4
|
+
class TaskRunner
|
5
|
+
|
6
|
+
extend Forwardable
|
7
|
+
|
8
|
+
attr_accessor :days
|
9
|
+
attr_accessor :klass
|
10
|
+
attr_accessor :task_options
|
11
|
+
|
12
|
+
def_delegators :klass, :find, :friendly_id_config
|
13
|
+
|
14
|
+
OLD_SLUG_DAYS = 45
|
15
|
+
|
16
|
+
def initialize(&block)
|
17
|
+
self.klass = ENV["MODEL"]
|
18
|
+
self.days = ENV["DAYS"]
|
19
|
+
end
|
20
|
+
|
21
|
+
def days=(days)
|
22
|
+
@days ||= days.blank? ? OLD_SLUG_DAYS : days.to_i
|
23
|
+
end
|
24
|
+
|
25
|
+
def klass=(klass)
|
26
|
+
@klass ||= klass.to_s.classify.constantize unless klass.blank?
|
27
|
+
end
|
28
|
+
|
29
|
+
def make_slugs
|
30
|
+
validate_uses_slugs
|
31
|
+
options = {:limit => 100, :slugs => nil, :order => [:id.asc]}.merge(task_options || {})
|
32
|
+
while records = klass.all(options) do
|
33
|
+
break if records.size == 0
|
34
|
+
records.each do |record|
|
35
|
+
record.send(:build_slug) # FIXME: DataMapper Hack: this hook is not getting called
|
36
|
+
record.save
|
37
|
+
yield(record) if block_given?
|
38
|
+
end
|
39
|
+
options.merge!(:id.gt => records.last.id)
|
40
|
+
end
|
41
|
+
end
|
42
|
+
|
43
|
+
def delete_slugs
|
44
|
+
validate_uses_slugs
|
45
|
+
Slug.all(:sluggable_type => klass).destroy!
|
46
|
+
if column = friendly_id_config.cache_column
|
47
|
+
klass.all.update!(column => nil)
|
48
|
+
end
|
49
|
+
end
|
50
|
+
|
51
|
+
def delete_old_slugs
|
52
|
+
conditions = ["created_at < ?", DateTime.now - days]
|
53
|
+
if klass
|
54
|
+
conditions[0] << " AND sluggable_type = ?"
|
55
|
+
conditions << klass.to_s
|
56
|
+
end
|
57
|
+
Slug.all(:conditions => conditions).select(&:outdated?).map(&:destroy)
|
58
|
+
end
|
59
|
+
|
60
|
+
def validate_uses_slugs
|
61
|
+
(raise "You need to pass a MODEL=<model name> argument to rake") if klass.blank?
|
62
|
+
unless friendly_id_config.use_slug?
|
63
|
+
raise "Class '%s' doesn't use slugs" % klass.to_s
|
64
|
+
end
|
65
|
+
rescue NoMethodError
|
66
|
+
raise "Class '%s' doesn't use FriendlyId" % klass.to_s
|
67
|
+
end
|
68
|
+
end
|
69
|
+
end
|
@@ -0,0 +1,34 @@
|
|
1
|
+
require 'dm-core'
|
2
|
+
require 'dm-transactions'
|
3
|
+
require 'dm-validations'
|
4
|
+
require 'friendly_id/datamapper_adapter/configuration'
|
5
|
+
require 'friendly_id/datamapper_adapter/slug'
|
6
|
+
require 'friendly_id/datamapper_adapter/simple_model'
|
7
|
+
require 'friendly_id/datamapper_adapter/slugged_model'
|
8
|
+
# require 'friendly_id/datamapper_adapter/tasks'
|
9
|
+
require 'forwardable'
|
10
|
+
|
11
|
+
module FriendlyId
|
12
|
+
module DataMapperAdapter
|
13
|
+
|
14
|
+
include FriendlyId::Base
|
15
|
+
|
16
|
+
def has_friendly_id(method, options = {})
|
17
|
+
extend FriendlyId::DataMapperAdapter::ClassMethods
|
18
|
+
@friendly_id_config = Configuration.new(self, method, options)
|
19
|
+
|
20
|
+
if friendly_id_config.use_slug?
|
21
|
+
include ::FriendlyId::DataMapperAdapter::SluggedModel
|
22
|
+
else
|
23
|
+
include ::FriendlyId::DataMapperAdapter::SimpleModel
|
24
|
+
end
|
25
|
+
end
|
26
|
+
|
27
|
+
module ClassMethods
|
28
|
+
attr_accessor :friendly_id_config
|
29
|
+
end
|
30
|
+
|
31
|
+
end
|
32
|
+
end
|
33
|
+
|
34
|
+
DataMapper::Model.append_extensions FriendlyId::DataMapperAdapter
|
@@ -0,0 +1,16 @@
|
|
1
|
+
require File.expand_path('../test_helper', __FILE__)
|
2
|
+
|
3
|
+
module FriendlyId
|
4
|
+
module Test
|
5
|
+
module DataMapperAdapter
|
6
|
+
|
7
|
+
# Tests for DataMapper models using FriendlyId with slugs.
|
8
|
+
class BasicSluggedModelTest < ::Test::Unit::TestCase
|
9
|
+
include FriendlyId::Test::Generic
|
10
|
+
include FriendlyId::Test::Slugged
|
11
|
+
include FriendlyId::Test::DataMapperAdapter::Slugged
|
12
|
+
include FriendlyId::Test::DataMapperAdapter::Core
|
13
|
+
end
|
14
|
+
end
|
15
|
+
end
|
16
|
+
end
|