public
Fork of caring/acts_as_url_param
Description: Pretty url support for Ruby on Rails applications
Homepage: http://www.caring.com
Clone URL: git://github.com/chriseppstein/acts_as_url_param.git
Search Repo:
putting acts_as_url_param up on git hub
Christopher Eppstein (author)
Wed Mar 19 07:06:27 -0700 2008
commit  614e13fae0f9c0628052b3d98b21560f49bb9ab6
tree    9d30b10e4c837098ad103ac4db826329b63074a1
0
...
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
0
...
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
0
@@ -0,0 +1,65 @@
0
+ActsAsUrlParam
0
+==============
0
+
0
+This plugin allows restful resources to be automatically exposed with human readable urls
0
+based on another method on the resource.
0
+
0
+The URL parameter value that is computed will be unique within the model. If the model is
0
+using inheritance, it's recommended to define acts_as_url_param on the base class.
0
+
0
+Defines one public class methods on your model:
0
+MyResource.url_param_available?(candidate,id=nil)
0
+# => true
0
+# if the candidate value does not exist for the model
0
+
0
+Defines one public instance method on your model:
0
+my_resource.compute_url_param
0
+# => "unique-url-parameter-2"
0
+
0
+Contributors
0
+============
0
+"Chris Eppstein"<chris@eppsteins.net>
0
+"Joshua Bates"<joshuabates@gmail.com>
0
+
0
+Example
0
+=======
0
+
0
+# use the url_param column as the model's url. It will be set and/or modified whenever the default method changes.
0
+# The default methods, in order of precedence, are: :name, :label, :title
0
+class MyResource < ActiveRecord::Base
0
+ acts_as_url_param
0
+end
0
+
0
+# use the url_name column as the model's url. It will be set and/or modified whenever title changes
0
+class MyResource < ActiveRecord::Base
0
+ acts_as_url_param :url_name, :from => :title
0
+end
0
+
0
+# use the url_param column as the model's url. It will be set and/or modified whenever title changes
0
+class MyResource < ActiveRecord::Base
0
+ acts_as_url_param :from => :title
0
+end
0
+
0
+# use the url_param column as the model's url. It will be set and/or modified whenever the :on event occurs.
0
+# Allowed events are: :create, :save, :update. Default is :create so that your permalinks stay permanent.
0
+# May also be a Proc that accepts the model instance and returns true if url_param should change.
0
+class MyResource < ActiveRecord::Base
0
+ acts_as_url_param :from => :title, :on => :create
0
+end
0
+
0
+# use the url_name column as the model's url. It will be set and/or modified whenever the :on event(s) occur.
0
+class MyResource < ActiveRecord::Base
0
+ acts_as_url_param :url_name, :from => :title, :on => :create
0
+end
0
+
0
+# Passing a block allows you to approve candidate url parameters.
0
+# This enables definition of a url space across that spans several models. This is easiest if all
0
+# models are acts_as_url_param.
0
+class MyResource < ActiveRecord::Base
0
+ acts_as_url_param :url_name, :from => :title do |candidate|
0
+ MyResource.url_param_available?(candidate,self.id) &&
0
+ MyOtherResource.url_param_available?(candidate)
0
+ end
0
+end
0
+
0
+Copyright (c) 2007 Chris Eppstein, released under the MIT license
0
\ No newline at end of file
...
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
...
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
0
@@ -0,0 +1,22 @@
0
+require 'rake'
0
+require 'rake/testtask'
0
+require 'rake/rdoctask'
0
+
0
+desc 'Default: run unit tests.'
0
+task :default => :test
0
+
0
+desc 'Test the acts_as_url_param plugin.'
0
+Rake::TestTask.new(:test) do |t|
0
+ t.libs << 'lib'
0
+ t.pattern = 'test/**/*_test.rb'
0
+ t.verbose = true
0
+end
0
+
0
+desc 'Generate documentation for the acts_as_url_param plugin.'
0
+Rake::RDocTask.new(:rdoc) do |rdoc|
0
+ rdoc.rdoc_dir = 'rdoc'
0
+ rdoc.title = 'ActsAsUrlParam'
0
+ rdoc.options << '--line-numbers' << '--inline-source'
0
+ rdoc.rdoc_files.include('README')
0
+ rdoc.rdoc_files.include('lib/**/*.rb')
0
+end
...
 
 
 
 
 
 
0
...
1
2
3
4
5
6
7
0
@@ -0,0 +1,6 @@
0
+# Include hook code here
0
+require "metaid"
0
+require "acts_as_url_param"
0
+require "redirect"
0
+require "url_utils"
0
+ActiveRecord::Base.send(:include, ActsAsUrlParam)
0
\ No newline at end of file
...
 
...
1
0
@@ -0,0 +1 @@
0
+# Install hook code here
...
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
0
...
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
0
@@ -0,0 +1,183 @@
0
+module ActsAsUrlParam
0
+ def self.included(base)
0
+ base.extend ActMethods
0
+ end
0
+
0
+ module ActMethods
0
+
0
+ def acts_as_url_param(*args, &block)
0
+ extend ClassMethods
0
+ include InstanceMethods
0
+ include Caring::Utilities::UrlUtils
0
+ extend Caring::Utilities::UrlUtils
0
+
0
+ class_inheritable_accessor :acts_as_url_options, :acts_as_url_param_base
0
+ # No extract options in rails 1.2.x
0
+ options = args.respond_to?(:extract_options!) ? args.extract_options! : extract_options_from_args!(args)
0
+ self.acts_as_url_options = options
0
+ options[:column] = args.first || 'url_name'
0
+ options[:from] ||= default_from_column
0
+
0
+ if options[:redirectable]
0
+ options[:on] ||= :update
0
+ make_redirectable
0
+ end
0
+
0
+ options[:on] ||= :create
0
+ options[:block] = block if block_given?
0
+ callback = "before_validation"
0
+ if options[:on] == :create
0
+ callback += "_on_create"
0
+ before_validation :set_url_param_if_non_existant
0
+ end
0
+ send callback, :set_url_param
0
+ validates_presence_of(options[:from], :if => :empty_param?) unless options[:allow_blank]
0
+
0
+ define_finder
0
+ define_url_param_setter
0
+ define_availability_check
0
+
0
+ self.class_eval do
0
+ alias_method_chain :validate, :unique_url unless method_defined? :validate_without_unique_url
0
+ end
0
+ end
0
+
0
+ private
0
+
0
+ def make_redirectable
0
+ has_many :redirects, :as => :redirectable
0
+ before_save :add_redirect
0
+
0
+ class_def :add_redirect do
0
+ if !new_record? && @name_changed && @old_name
0
+ redirects.create(:url_name => @old_name)
0
+ end
0
+ end
0
+
0
+ meta_def :find_redirect do |name|
0
+ redirect = Redirect.find_by_class_and_name(self,name)
0
+ redirect.redirectable if redirect
0
+ end
0
+ end
0
+
0
+ def define_finder
0
+ meta_def :find_by_url do |*args|
0
+ send("find_by_#{acts_as_url_options[:column]}", *args)
0
+ end
0
+ end
0
+
0
+ def define_url_param_setter
0
+ class_def "#{acts_as_url_options[:column]}=" do |value|
0
+ @url_name_manually_set = true if value
0
+ @old_name = read_attribute(acts_as_url_options[:column]) unless @name_changed
0
+ write_attribute(acts_as_url_options[:column], url_safe(value))
0
+ @name_changed = true unless read_attribute(acts_as_url_options[:column]) == @old_name || !@old_name
0
+ end
0
+ end
0
+
0
+ def define_availability_check
0
+ klass = self
0
+ meta_def :url_param_available_for_model? do |*args|
0
+ candidate, id = *args
0
+ conditions = acts_as_url_options[:conditions] + ' AND ' if acts_as_url_options[:conditions]
0
+ conditions ||= ''
0
+ conditions += "#{acts_as_url_options[:column]} = ?"
0
+ conditions += " AND id != ?" if id
0
+ conditions = [conditions, candidate]
0
+ conditions << id if id
0
+ available = if descends_from_active_record? or self == klass
0
+ count(:conditions => conditions) == 0
0
+ else
0
+ base_class.count(:conditions => conditions) == 0
0
+ end
0
+ if acts_as_url_options[:redirectable] && available
0
+ re_conditions = "url_name = ? AND redirectable_class = ?"
0
+ re_conditions += "AND redirectable_id != ?" if id
0
+ re_conditions = [re_conditions, candidate, self.to_s]
0
+ re_conditions << id if id
0
+ available = Redirect.count(:conditions => re_conditions) == 0
0
+ end
0
+ available
0
+ end
0
+ end
0
+
0
+ def default_from_column
0
+ %W(name label title).detect do |column_name|
0
+ column_or_method_exists?(column_name) and self.acts_as_url_options[:to].to_s != column_name
0
+ end
0
+ end
0
+
0
+ def column_or_method_exists?(name)
0
+ column_names.include? name.to_s or method_defined? name
0
+ end
0
+
0
+ module ClassMethods
0
+ def url_param_available?(candidate, id=nil)
0
+ Caring::Scope::State.instance.with_admin_scope(true) do
0
+ if proc = acts_as_url_options[:block]
0
+ !(proc.arity == 1 ? proc.call(candidate) : proc.call(candidate, id))
0
+ else
0
+ url_param_available_for_model?(candidate, id)
0
+ end
0
+ end
0
+ end
0
+
0
+ def compute_url_param(candidate, id=nil)
0
+ return if candidate.blank?
0
+ # raise ArgumentError, "The url canidate cannot be empty" if candidate.blank?
0
+ uniquify_proc = acts_as_url_options[:block] || Proc.new { |candidate| url_param_available? candidate, id }
0
+ uniquify(url_safe(candidate), &uniquify_proc)
0
+ end
0
+ end
0
+
0
+ module InstanceMethods
0
+ def compute_url_param
0
+ # raise ArgumentError, "The column used for generating the url_param is empty" unless url_from
0
+ self.class.compute_url_param(url_from, id)
0
+ end
0
+
0
+ def url_from
0
+ self.class.method_defined?(acts_as_url_options[:from]) ? send(acts_as_url_options[:from]) : read_attribute(acts_as_url_options[:from])
0
+ end
0
+
0
+ def to_param
0
+ url_param || id.to_s
0
+ end
0
+
0
+ def url_param
0
+ read_attribute(acts_as_url_options[:column])
0
+ end
0
+
0
+ def empty_param?
0
+ !url_param
0
+ end
0
+
0
+ private
0
+
0
+ def set_url_param_if_non_existant
0
+ unless new_record?
0
+ set_url_param if url_param.blank?
0
+ end
0
+ end
0
+
0
+ def set_url_param
0
+ if url_param.blank? or (acts_as_url_options[:on] != :create && !@url_name_manually_set)
0
+ send(acts_as_url_options[:before]) if acts_as_url_options[:before]
0
+ url = compute_url_param
0
+ send("#{acts_as_url_options[:column]}=", url) unless url.blank?
0
+ @url_name_manually_set = false
0
+ @url_param_validated = true
0
+ end
0
+ end
0
+
0
+ def validate_with_unique_url
0
+ return true if @url_param_validated
0
+ avail_id = new_record? ? nil : id
0
+ unless self.class.url_param_available? to_param, avail_id
0
+ errors.add_to_base "The url is not unique"
0
+ end
0
+ validate_without_unique_url
0
+ end
0
+ end
0
+ end
0
+end
0
\ No newline at end of file
...
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
0
...
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
0
@@ -0,0 +1,15 @@
0
+class Object
0
+ # The hidden singleton lurks behind everyone
0
+ def metaclass; class << self; self; end; end
0
+ def meta_eval &blk; metaclass.instance_eval &blk; end
0
+
0
+ # Adds methods to a metaclass
0
+ def meta_def name, &blk
0
+ meta_eval { define_method name, &blk }
0
+ end
0
+
0
+ # Defines an instance method within a class
0
+ def class_def name, &blk
0
+ class_eval { define_method name, &blk }
0
+ end
0
+end
0
\ No newline at end of file
...
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
...
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
0
@@ -0,0 +1,22 @@
0
+# redirectable_type
0
+# redirectable_id
0
+# redirectable_class
0
+# name
0
+
0
+class Redirect < ActiveRecord::Base
0
+ belongs_to :redirectable, :polymorphic => true
0
+ before_create :set_real_class
0
+
0
+ def self.find_by_class_and_name(klass,name)
0
+ find(:first, :conditions => ["redirectable_class = ? AND url_name = ?", klass.to_s, name], :order => "created_at desc")
0
+ end
0
+
0
+ def to_label
0
+ url_name
0
+ end
0
+
0
+ private
0
+ def set_real_class
0
+ self.redirectable_class = redirectable.class.to_s
0
+ end
0
+end
...
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
...
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
0
@@ -0,0 +1,70 @@
0
+require 'generator'
0
+
0
+module Caring
0
+ module Utilities
0
+ module UrlUtils
0
+ # Makes a string safe for urls
0
+ # Options:
0
+ # :replacements - a Hash of a replacement string to a regex that should match for replacement
0
+ # :char - when :replacements is not provided, this is the string that will be used to replace unsafe characters. defaults to '-'
0
+ # :collapse - set to false if multiple, consecutive unsafe characters should not be replaced with only a single instance of :char.
0
+ # defaults to true. only matters if you don't provide :replacements. If you do, you'll need to write your regex such
0
+ # that it matches multiple characters.
0
+ # :strip_endings - remove replacement characters from the beginning and end of the safe string. Defaults to true.
0
+ # :downcase - make the input string lower case. Defautls to true.
0
+ def url_safe(s, options = {})
0
+ return s if s.blank?
0
+ s = s.downcase if options.fetch(:downcase, true)
0
+ collapse = options.fetch(:collapse, true)
0
+ default_regex = /[^'a-zA-Z0-9]#{"+" if collapse}/
0
+ s.gsub! /(&amp;)|&/, 'and'
0
+ replacements = options[:replacements] || { options.fetch(:char,"-") => default_regex, "" => /'#{"+" if collapse}/}
0
+ replacements.each do |replacement, regex|
0
+ s = s.gsub(regex,replacement)
0
+ end
0
+ if options.fetch(:strip_endings,true)
0
+ replacement_strings = replacements.keys.map{|k| "(#{Regexp.escape(k)})" unless k.blank?}.compact.join("|")
0
+ s = s.gsub(/(^#{replacement_strings})|(#{replacement_strings}$)/,"")
0
+ end
0
+ return s
0
+ end
0
+
0
+ # Generate integers
0
+ # Options:
0
+ # :start => 1, The integer to start with
0
+ # :end => nil, the last integer to generate, when nil this becomes an infinite sequence
0
+ # :increment => 1, the amount to add for each iteration
0
+ def int_generator(options = {})
0
+ start = options.fetch(:start,1)
0
+ last = options[:end]
0
+ increment = options.fetch(:increment, 1)
0
+ raise ArgumentError if increment == 0
0
+ raise ArgumentError if last && (increment > 0 && start > last) || (increment < 0 && start < last)
0
+ Generator.new do |g|
0
+ i = start
0
+ loop do
0
+ g.yield i
0
+ return if !last.nil? && (increment > 0 && i >= last) || (increment < 0 && i <= last)
0
+ i = i + increment
0
+ end
0
+ end
0
+ end
0
+
0
+ # accepts a block that will be passed a candidate string and should return true if it is unique.
0
+ # Options:
0
+ # => :separator => "-", a string that will be injected between the base string and the uniqifier
0
+ # => :endings => generator, a Generator that provides endings to be placed at the end of the base.
0
+ # defaults to the set of positive integers.
0
+ def uniquify(base, options = {})
0
+ sep = options.fetch(:separator, "-")
0
+ endings = options[:endings] || int_generator
0
+ return base if yield base
0
+ while endings.next? do
0
+ candidate = base+sep+endings.next.to_s
0
+ return candidate if yield candidate
0
+ end
0
+ raise ArgumentError.new("No unique construction found for \"#{base}\"")
0
+ end
0
+ end
0
+ end
0
+end
...
 
 
 
 
0
...
1
2
3
4
5
0
@@ -0,0 +1,4 @@
0
+# desc "Explaining what the task does"
0
+# task :acts_as_url_param do
0
+# # Task goes here
0
+# end
0
\ No newline at end of file
...
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
0
...
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
0
@@ -0,0 +1,202 @@
0
+require File.expand_path(File.dirname(__FILE__) + "/test_helper")
0
+require "mocha"
0
+
0
+require "acts_as_url_param_base"
0
+require "author"
0
+require "blog_post"
0
+require "item"
0
+require "book"
0
+require "magazine"
0
+require "newspaper"
0
+require "user"
0
+require "story"
0
+require "before_item"
0
+
0
+class ActsAsUrlParamTest < Test::Unit::TestCase
0
+
0
+ def test_should_define_finder
0
+ item = ActsAsUrlParam::Item.create(:name => 'just try and find me')
0
+ assert_equal item, ActsAsUrlParam::Item.find_by_url(item.to_param)
0
+ end
0
+
0
+ def test_should_set_url_name_on_create
0
+ assert !ActsAsUrlParam::Item.create(:name => 'test a url param').url_name.blank?
0
+ end
0
+
0
+ def test_should_not_set_url_name_if_already_set
0
+ item = ActsAsUrlParam::Item.create(:name => 'test a url param', :url_name => 'test-this')
0
+ assert_equal('test-this', item.to_param)
0
+ end
0
+
0
+ def test_should_make_manually_set_urls_safe
0
+ item = ActsAsUrlParam::Item.create(:name => 'test a manual url param', :url_name => 'test this one')
0
+ assert_equal('test-this-one', item.to_param)
0
+ end
0
+
0
+ def test_should_set_url_name_if_blank
0
+ item = ActsAsUrlParam::Item.create(:name => 'no url param')
0
+ item.send(:write_attribute, :url_name, nil)
0
+ item.save
0
+ assert item.url_name
0
+ end
0
+
0
+ def test_should_make_custom_urls_safe
0
+ ActsAsUrlParam::Item.any_instance.expects(:url_safe)
0
+ ActsAsUrlParam::Item.new(:url_name => 'make me safe')
0
+ end
0
+
0
+ def test_should_use_correct_column_to_create_url_name
0
+ assert ActsAsUrlParam::User.create(:name => 'john doe', :login => 'jdog').url_name =~ /jdog/
0
+ end
0
+
0
+ def test_should_be_invalid_without_content_to_create_url
0
+ user = ActsAsUrlParam::User.create(:name => 'john doe')
0
+ assert !user.valid?
0
+ end
0
+
0
+ def test_should_allow_blank_url_param_if_specified
0
+ item = ActsAsUrlParam::Story.create
0
+ assert item.valid?
0
+ end
0
+
0
+ def test_should_check_if_url_param_available
0
+ ActsAsUrlParam::User.create(:login => 'tester')
0
+ assert !ActsAsUrlParam::User.url_param_available?('tester')
0
+ assert ActsAsUrlParam::User.url_param_available?('goodman')
0
+ end
0
+
0
+ def test_should_use_block_to_check_if_url_param_available
0
+ ActsAsUrlParam::Story.create(:title => 'new post')
0
+ assert !ActsAsUrlParam::BlogPost.url_param_available?('new post')
0
+ end
0
+
0
+ def test_should_use_block_to_compute_url_name
0
+ post = ActsAsUrlParam::BlogPost.new(:title => 'post')
0
+ assert_equal 'post', post.compute_url_param
0
+ story = ActsAsUrlParam::Story.create(:title => 'new post')
0
+ new_post = ActsAsUrlParam::BlogPost.new(:title => 'new post')
0
+ assert_not_equal new_post.compute_url_param, story.to_param
0
+ end
0
+
0
+ def test_should_create_redirect_trail
0
+ name = "this is a redirectable item"
0
+ item = ActsAsUrlParam::Item.create(:name => name)
0
+ url = item.url_name
0
+ assert_equal 0, item.redirects.size
0
+ item.update_attributes :name => "redirect to me"
0
+ assert_equal 1, item.redirects.count
0
+ assert_equal url, item.redirects.first.url_name
0
+ end
0
+
0
+ def test_should_create_redirect_trail_with_manual_url
0
+ name = "this is another redirectable item"
0
+ item = ActsAsUrlParam::Item.create(:name => name)
0
+ url = item.url_name
0
+ assert_equal 0, item.redirects.size
0
+ item.update_attributes :url_name => "a-new-url-for-you"
0
+ assert_equal 1, item.redirects.count
0
+ assert_equal url, item.redirects.first.url_name
0
+ end
0
+
0
+ def test_should_find_from_url_trail
0
+ name = "this is a redirectable item"
0
+ item = ActsAsUrlParam::Item.create(:name => name)
0
+ url = item.url_name
0
+ item.update_attributes :name => "redirect to me"
0
+ assert item.to_param != url
0
+ assert_equal item, ActsAsUrlParam::Item.find_redirect(url)
0
+ end
0
+
0
+ def test_should_check_redirects_table_for_available_names
0
+ name = "this is a redirectable item"
0
+ item = ActsAsUrlParam::Book.create(:name => name)
0
+ url = item.url_name
0
+ item.update_attributes(:name => "second one")
0
+ assert !ActsAsUrlParam::Book.url_param_available?(url)
0
+ assert ActsAsUrlParam::Magazine.url_param_available?(url)
0
+ end
0
+
0
+ def test_should_compute_url_name
0
+ name = 'this is a url param'
0
+ item = ActsAsUrlParam::Item.new(:name => name)
0
+ assert !ActsAsUrlParam::Item.compute_url_param(name).blank?
0
+ assert_equal(item.compute_url_param, ActsAsUrlParam::Item.compute_url_param(name))
0
+ end
0
+
0
+ def test_should_update_url_name_on_custom_callback
0
+ author = ActsAsUrlParam::Author.create(:label => 'name of author')
0
+ author_url = author.to_param
0
+ author.update_attributes(:label => 'a new author')
0
+ assert_not_equal(author_url, author.to_param)
0
+ end
0
+
0
+ def test_should_not_update_url_name_on_custom_callback_when_no_change
0
+ author = ActsAsUrlParam::Author.create(:label => 'name of author')
0
+ author_url = author.to_param
0
+ author.update_attributes(:bio => 'unrelated to url param')
0
+ assert_equal(author_url, author.to_param)
0
+ end
0
+
0
+ def test_should_not_set_existing_url_name_to_blank
0
+ author = ActsAsUrlParam::Author.create(:label => 'name of author')
0
+ author_url = author.to_param
0
+ author.update_attributes(:label => '')
0
+ assert_equal(author_url, author.to_param)
0
+ end
0
+
0
+ def test_should_not_update_url_name_by_default
0
+ item = ActsAsUrlParam::Newspaper.create(:name => 'this is a url param')
0
+ item_url = item.to_param
0
+ item.update_attributes(:name => 'not updated')
0
+ assert_equal(item_url, item.to_param)
0
+ end
0
+
0
+ def test_should_work_with_two_items_of_same_name
0
+ name = "just another name"
0
+ item = ActsAsUrlParam::Item.create(:name => name)
0
+ url = item.to_param
0
+ item.update_attributes(:content => "irrelevant")
0
+ assert_equal url, item.to_param
0
+ end
0
+
0
+ def test_should_work_with_sti
0
+ item = ActsAsUrlParam::Item.create(:name => 'an item')
0
+ book = ActsAsUrlParam::Book.create(:name => 'an item')
0
+ newspaper = ActsAsUrlParam::Newspaper.create(:name => 'an item')
0
+ assert_not_equal(item.to_param, book.to_param)
0
+ assert_equal(item.to_param, newspaper.to_param)
0
+
0
+ newspaper = ActsAsUrlParam::Newspaper.create(:name => 'another item')
0
+ book = ActsAsUrlParam::Book.create(:name => 'another item')
0
+ assert_equal(book.to_param, newspaper.to_param)
0
+ end
0
+
0
+ def test_should_use_method_for_url_from_if_exists
0
+ magazine = ActsAsUrlParam::Magazine.create()
0
+ assert !magazine.to_param.blank?
0
+ end
0
+
0
+ def test_should_run_before_method_if_passed
0
+ ActsAsUrlParam::BeforeItem.any_instance.expects(:set_name)
0
+ item = ActsAsUrlParam::BeforeItem.create(:name => 'the name')
0
+ end
0
+
0
+ def test_should_run_before_method_before_setting_url
0
+ item = ActsAsUrlParam::BeforeItem.create(:name => 'the name')
0
+ assert_match /is-set/, item.to_param
0
+ end
0
+
0
+ private
0
+ def acts_as_url_name_model(column = nil, options = {})
0
+ m = Class.new(ActiveRecord::Base)
0
+ m.class_eval do
0
+ set_table_name :items
0
+ if column
0
+ acts_as_url_param column, options
0
+ else
0
+ acts_as_url_param options
0
+ end
0
+ end
0
+ m
0
+ end
0
+end
0
\ No newline at end of file
...
 
 
 
0
...
1
2
3
4
0
@@ -0,0 +1,3 @@
0
+sqlite3:
0
+ :adapter: sqlite3
0
+ :dbfile: ":memory:"
0
\ No newline at end of file
...
 
 
 
 
 
 
 
 
0
...
1
2
3
4
5
6
7
8
9
0
@@ -0,0 +1,8 @@
0
+# Namespace models
0
+module ActsAsUrlParam
0
+end
0
+
0
+class ActsAsUrlParamBase < ActiveRecord::Base
0
+ self.abstract_class = true
0
+ self.connection = ACTS_AS_URL_PARAM_TEST_DB
0
+end
0
\ No newline at end of file
...
 
 
 
0
...
1
2
3
4
0
@@ -0,0 +1,3 @@
0
+class ActsAsUrlParam::Author < ActsAsUrlParamBase
0
+ acts_as_url_param :on => :update, :allow_blank => true
0
+end
0
\ No newline at end of file
...
 
 
 
 
 
 
 
...
1
2
3
4
5
6
7
0
@@ -0,0 +1,7 @@
0
+class ActsAsUrlParam::BeforeItem < ActsAsUrlParam::Item
0
+ acts_as_url_param :before => :set_name
0
+
0
+ def set_name
0
+ self.name += " is set"
0
+ end
0
+end
...
 
 
 
 
 
 
0
...
1
2
3
4
5
6
7
0
@@ -0,0 +1,6 @@
0
+class ActsAsUrlParam::BlogPost < ActsAsUrlParamBase
0
+ acts_as_url_param do |candidate|
0
+ url_param_available_for_model?(candidate) &&
0
+ Story.url_param_available?(candidate)
0
+ end
0
+end
0
\ No newline at end of file
...
 
 
0
...
1
2
3
0
@@ -0,0 +1,2 @@
0
+class ActsAsUrlParam::Book < ActsAsUrlParam::Item
0
+end
0
\ No newline at end of file
...
 
 
 
0
...
1
2
3
4</