ramaze 2008.11 → 2009.01

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (110) hide show
  1. data/README.markdown +7 -7
  2. data/benchmark/run.rb +1 -1
  3. data/doc/CHANGELOG +662 -0
  4. data/examples/app/blog/model/entry.rb +8 -1
  5. data/examples/app/blog/spec/blog.rb +2 -2
  6. data/examples/app/rapaste/spec/rapaste.rb +1 -1
  7. data/examples/app/rapaste/start.rb +2 -2
  8. data/examples/app/rapaste/view/view.xhtml +3 -0
  9. data/examples/app/todolist/spec/todolist.rb +1 -1
  10. data/examples/app/whywiki/spec/whywiki.rb +1 -1
  11. data/examples/app/wikore/spec/wikore.rb +1 -1
  12. data/examples/app/wikore/src/model.rb +8 -2
  13. data/examples/app/wiktacular/spec/wiktacular.rb +1 -1
  14. data/examples/app/wiktacular/src/model.rb +1 -1
  15. data/examples/basic/partial.rb +28 -0
  16. data/examples/helpers/httpdigest.rb +68 -10
  17. data/examples/misc/ramaise.rb +2 -2
  18. data/examples/templates/template_amrita2.rb +1 -1
  19. data/examples/templates/template_erubis.rb +1 -1
  20. data/examples/templates/template_ezamar.rb +1 -1
  21. data/examples/templates/template_haml.rb +2 -2
  22. data/examples/templates/template_liquid.rb +1 -1
  23. data/examples/templates/template_markaby.rb +2 -2
  24. data/examples/templates/template_nagoro.rb +1 -1
  25. data/examples/templates/template_redcloth.rb +1 -1
  26. data/examples/templates/template_remarkably.rb +2 -2
  27. data/examples/templates/template_tenjin.rb +1 -1
  28. data/examples/templates/template_xslt.rb +1 -1
  29. data/lib/proto/controller/init.rb +2 -1
  30. data/lib/proto/model/init.rb +3 -3
  31. data/lib/proto/public/dispatch.fcgi +2 -2
  32. data/lib/proto/spec/main.rb +3 -3
  33. data/lib/proto/start.rb +4 -0
  34. data/lib/ramaze.rb +6 -0
  35. data/lib/ramaze/action.rb +7 -1
  36. data/lib/ramaze/action/render.rb +6 -5
  37. data/lib/ramaze/cache.rb +1 -0
  38. data/lib/ramaze/cache/file.rb +71 -0
  39. data/lib/ramaze/contrib.rb +1 -1
  40. data/lib/ramaze/contrib/email.rb +2 -0
  41. data/lib/ramaze/contrib/facebook.rb +2 -2
  42. data/lib/ramaze/contrib/file_cache.rb +2 -64
  43. data/lib/ramaze/contrib/sequel/image.rb +1 -1
  44. data/lib/ramaze/controller.rb +9 -1
  45. data/lib/ramaze/controller/resolve.rb +10 -5
  46. data/lib/ramaze/current/request.rb +87 -70
  47. data/lib/ramaze/current/session.rb +3 -5
  48. data/lib/ramaze/current/session/hash.rb +7 -11
  49. data/lib/ramaze/dispatcher/action.rb +2 -0
  50. data/lib/ramaze/dispatcher/file.rb +6 -1
  51. data/lib/ramaze/helper.rb +12 -4
  52. data/lib/ramaze/helper/aspect.rb +2 -2
  53. data/lib/ramaze/helper/bench.rb +43 -0
  54. data/lib/ramaze/helper/form.rb +5 -2
  55. data/lib/ramaze/helper/formatting.rb +4 -0
  56. data/lib/ramaze/helper/gravatar.rb +18 -1
  57. data/lib/ramaze/helper/httpdigest.rb +55 -28
  58. data/lib/ramaze/helper/markaby.rb +1 -1
  59. data/lib/ramaze/helper/maruku.rb +2 -0
  60. data/lib/ramaze/helper/paginate.rb +1 -1
  61. data/lib/ramaze/helper/partial.rb +1 -1
  62. data/lib/ramaze/helper/redirect.rb +22 -4
  63. data/lib/ramaze/helper/user.rb +4 -4
  64. data/lib/ramaze/option.rb +1 -1
  65. data/lib/ramaze/option/holder.rb +3 -3
  66. data/lib/ramaze/reloader.rb +25 -41
  67. data/lib/ramaze/reloader/watch_inotify.rb +85 -0
  68. data/lib/ramaze/reloader/watch_stat.rb +58 -0
  69. data/lib/ramaze/snippets/divide.rb +2 -0
  70. data/lib/ramaze/snippets/numeric/time.rb +1 -1
  71. data/lib/ramaze/snippets/object/__dir__.rb +3 -3
  72. data/lib/ramaze/snippets/object/acquire.rb +3 -6
  73. data/lib/ramaze/snippets/ramaze/acquire.rb +31 -0
  74. data/lib/ramaze/snippets/ramaze/deprecated.rb +2 -1
  75. data/lib/ramaze/spec/helper/mock_http.rb +6 -5
  76. data/lib/ramaze/template/ezamar/render_partial.rb +8 -0
  77. data/lib/ramaze/tool/mime.rb +1 -1
  78. data/lib/ramaze/tool/project_creator.rb +2 -1
  79. data/lib/ramaze/version.rb +2 -2
  80. data/rake_tasks/coverage.rake +4 -5
  81. data/rake_tasks/spec.rake +6 -6
  82. data/ramaze-2008.11.gem +0 -0
  83. data/ramaze.gemspec +759 -758
  84. data/spec/contrib/profiling.rb +2 -2
  85. data/spec/ramaze/action/file_cache.rb +1 -1
  86. data/spec/ramaze/action/layout.rb +1 -1
  87. data/spec/ramaze/controller/actionless_templates.rb +1 -1
  88. data/spec/ramaze/controller/resolve.rb +1 -1
  89. data/spec/ramaze/controller/template_resolving.rb +1 -1
  90. data/spec/ramaze/dispatcher/directory.rb +3 -3
  91. data/spec/ramaze/helper/aspect.rb +1 -1
  92. data/spec/ramaze/helper/partial.rb +1 -1
  93. data/spec/ramaze/localize.rb +1 -1
  94. data/spec/ramaze/rewrite.rb +1 -1
  95. data/spec/ramaze/template.rb +3 -3
  96. data/spec/ramaze/template/amrita2.rb +1 -1
  97. data/spec/ramaze/template/erubis.rb +1 -1
  98. data/spec/ramaze/template/ezamar.rb +1 -1
  99. data/spec/ramaze/template/haml.rb +2 -2
  100. data/spec/ramaze/template/nagoro.rb +1 -1
  101. data/spec/ramaze/template/redcloth.rb +1 -1
  102. data/spec/ramaze/template/sass.rb +1 -1
  103. data/spec/ramaze/template/tenjin.rb +1 -1
  104. data/spec/snippets/object/__dir__.rb +6 -0
  105. data/spec/snippets/{object → ramaze}/acquire.rb +24 -18
  106. metadata +18 -16
  107. data/lib/ramaze/contrib/auto_params.rb +0 -135
  108. data/lib/ramaze/contrib/auto_params/get_args.rb +0 -58
  109. data/spec/contrib/auto_params.rb +0 -121
  110. data/spec/snippets/divide.rb +0 -19
@@ -96,7 +96,7 @@ module Ramaze
96
96
  def initialize(sess_or_request = Current.request)
97
97
  return unless Global.sessions
98
98
 
99
- if sess_or_request.is_a?(Request)
99
+ if sess_or_request.respond_to?(:cookies)
100
100
  request = sess_or_request
101
101
  @session_id = request.cookies[SESSION_KEY] || Session.random_key
102
102
  else
@@ -125,10 +125,8 @@ module Ramaze
125
125
  # existing already, the session itself is an instance of SessionHash
126
126
 
127
127
  def current
128
- unless @current
129
- @current = ( sessions[session_id] ||= Session::Hash.new(self) )
130
- end
131
- @current
128
+ return @current if @current
129
+ @current = ( sessions[session_id] ||= Session::Hash.new(self) )
132
130
  end
133
131
 
134
132
  # shortcut to Cache.sessions
@@ -9,8 +9,8 @@ module Ramaze
9
9
 
10
10
  # Sets @hash to an empty Hash
11
11
 
12
- def initialize sess
13
- @session = sess
12
+ def initialize(session)
13
+ @session = session
14
14
  @hash = {}
15
15
  end
16
16
 
@@ -18,11 +18,8 @@ module Ramaze
18
18
  # Session.current.sessions if anything changes.
19
19
 
20
20
  def method_missing(*args, &block)
21
- old = @hash.dup
22
21
  result = @hash.send(*args, &block)
23
- unless old == @hash
24
- Cache.sessions[@session.session_id] = self
25
- end
22
+ Cache.sessions[@session.session_id] = self
26
23
  result
27
24
  end
28
25
 
@@ -48,11 +45,10 @@ module Ramaze
48
45
 
49
46
  # Unmarshal cookie data to a hash and verify its integrity.
50
47
  def unmarshal(cookie)
51
- if cookie
52
- data, digest = cookie.split('--')
53
- return nil unless digest == generate_digest(data)
54
- Marshal.load(data.unpack('m').first)
55
- end
48
+ return unless cookie
49
+ data, digest = cookie.split('--')
50
+ return nil unless digest == generate_digest(data)
51
+ Marshal.load(data.unpack('m').first)
56
52
  end
57
53
 
58
54
  # Generate the inline SHA512 message digest. Larger (128 bytes) than SHA256
@@ -35,6 +35,8 @@ module Ramaze
35
35
  ex
36
36
  end
37
37
 
38
+ # Logs the request via Log#info unless it's boring.
39
+
38
40
  def log(path)
39
41
  case path
40
42
  when *Global.boring
@@ -64,7 +64,8 @@ module Ramaze
64
64
  joined = ::File.join(Global.public_root, path)
65
65
 
66
66
  if ::File.directory?(joined)
67
- Dir[joined/"{#{INDICES.join(',')}}"].first || joined
67
+ glob = ::File.join(joined, "{#{INDICES.join(',')}}")
68
+ Dir[glob].first || joined
68
69
  else
69
70
  joined
70
71
  end
@@ -78,6 +79,10 @@ module Ramaze
78
79
 
79
80
  private
80
81
 
82
+ # Shortcut for Ramaze::Dispatcher::File#expand_path(path).
83
+ # Returns the absolute path of passed argument e.g.
84
+ # "/Users/rikur/ramaze_tests/public/foo.txt"
85
+
81
86
  def expand(path)
82
87
  ::File.expand_path(path)
83
88
  end
@@ -12,8 +12,9 @@ module Ramaze
12
12
  module Helper
13
13
  LOOKUP = Set.new
14
14
  PATH = ['']
15
+ path = File.expand_path("#{BASEDIR}/../spec/ramaze/helper/")
15
16
  trait :ignore => [
16
- /#{Regexp.escape(File.expand_path(BASEDIR/'../spec/ramaze/helper/'))}\//
17
+ /#{Regexp.escape(path)}\//
17
18
  ]
18
19
 
19
20
  module Methods
@@ -44,7 +45,7 @@ module Ramaze
44
45
  if require_helper(name)
45
46
  redo
46
47
  else
47
- raise LoadError, "#{name} not found"
48
+ raise LoadError, "helper #{name} not found"
48
49
  end
49
50
  end
50
51
  end
@@ -52,6 +53,8 @@ module Ramaze
52
53
 
53
54
  private
54
55
 
56
+ # returns the Ramaze::Helper::Name Module Constant if exists.
57
+
55
58
  def find_helper(name)
56
59
  name = name.to_s.camel_case
57
60
  ramaze_helper_consts = ::Ramaze::Helper.constants.grep(/^#{name}$/i)
@@ -60,16 +63,21 @@ module Ramaze
60
63
  end
61
64
  end
62
65
 
66
+ # Loads helper from /lib/ramaze/helper/name.(so, bundle, rb)
67
+ # raises LoadError if helper not found.
68
+
63
69
  def require_helper(name)
64
- paths = (PATH + [Global.root, BASEDIR/:ramaze]).join(',')
70
+ paths = (PATH + [Global.root, "#{BASEDIR}/ramaze"]).join(',')
65
71
  glob = "{#{paths}}/helper/#{name}.{so,bundle,rb}"
66
72
  files = Dir[glob]
67
73
  ignore = Helper.trait[:ignore]
68
74
  files.reject!{|f| ignore.any?{|i| f =~ i }}
69
- raise LoadError, "#{name} not found" unless file = files.first
75
+ raise LoadError, "file for #{name} not found" unless file = files.first
70
76
  require(file)
71
77
  end
72
78
 
79
+ # injects the helper via include and extend, takes Constant as argument.
80
+
73
81
  def use_helper(mod)
74
82
  include mod
75
83
  extend mod
@@ -76,7 +76,7 @@ module Ramaze
76
76
  class Action
77
77
 
78
78
  # overwrites the default Action hook and runs the neccesary blocks in its
79
- # scope. before actions are run starting from Ramaze::Controller down the
79
+ # scope before actions are run, starting from Ramaze::Controller down the
80
80
  # ancestor chain.
81
81
  def before_process
82
82
  common_aspect(:before)
@@ -84,7 +84,7 @@ module Ramaze
84
84
 
85
85
 
86
86
  # overwrites the default Action hook and runs the neccesary blocks in its
87
- # scope. before actions are run starting from Ramaze::Controller down the
87
+ # scope after actions are run, starting from Ramaze::Controller down the
88
88
  # ancestor chain.
89
89
  def after_process
90
90
  common_aspect(:after)
@@ -0,0 +1,43 @@
1
+ require 'benchmark'
2
+
3
+ module Ramaze
4
+ module Helper
5
+
6
+ # Little helper to give you a hand when benching parts of actions
7
+
8
+ module Bench
9
+
10
+ # Will first run an empty loop to determine the overhead it imposes, then
11
+ # goes on to yield your block +iterations+ times.
12
+ #
13
+ # The last yielded return value will be returned upon completion of the
14
+ # benchmark and the result of the benchmark itself will be sent to
15
+ # Log.info
16
+ #
17
+ # Example:
18
+ #
19
+ # class MainController < Ramaze::Controller
20
+ # def index
21
+ # @users = bench{ User.all }
22
+ # @tags = bench{ Article.tags }
23
+ # end
24
+ # end
25
+ #
26
+ # This will show something like following in your log:
27
+ # [..] INFO Bench ./start.rb:3:in `index': 0.121163845062256
28
+ # [..] INFO Bench ./start.rb:4:in `index': 2.234987235098341
29
+ #
30
+ # So now we know that the Article.tags call takes the most time and
31
+ # should be improved.
32
+
33
+ def bench(iterations = 1)
34
+ result = nil
35
+ from = caller[0]
36
+ delta = Benchmark.realtime{ iterations.times{ nil }}
37
+ taken = Benchmark.realtime{ iterations.times{ result = yield }}
38
+ Log.info "Bench #{from}: #{taken - delta}"
39
+ return result
40
+ end
41
+ end
42
+ end
43
+ end
@@ -219,16 +219,19 @@ module Ramaze
219
219
 
220
220
  # Form for instances of the model class
221
221
  class InstanceForm < Form
222
- # <input type='text' name='name' value='value' />
222
+ # returns <input type='text' name='name' value='value' />
223
223
  def field_input(name, value)
224
224
  "<input type='text' name='#{name}' value='#{value}'/>"
225
225
  end
226
226
 
227
+ # returns <textarea name='name'>#{value}</textarea>
228
+
227
229
  def field_textarea(name, value)
228
230
  "<textarea name='#{name}'>#{value}</textarea>"
229
231
  end
230
232
 
231
- # <input type="text" name="name" value="value" />
233
+ # returns <input type="text" name="name" value="value" />
234
+
232
235
  def field_integer(name, value)
233
236
  field_input(name, value)
234
237
  end
@@ -111,6 +111,10 @@ module Ramaze
111
111
  end
112
112
  alias autolink auto_link
113
113
 
114
+ # takes a string and optional argument for outputting compliance HTML
115
+ # instead of XHTML.
116
+ # e.g nl2br "a\nb\n\c" #=> 'a<br />b<br />c'
117
+
114
118
  def nl2br(string, xhtml = true)
115
119
  br = xhtml ? '<br />' : '<br>'
116
120
  string.gsub(/\n/, br)
@@ -1,12 +1,29 @@
1
1
  module Ramaze
2
2
  module Helper
3
3
  module Gravatar
4
+
5
+ # fetches a gravatar from http//www.gravatar.com based on 'email'
6
+ # and 'size'. Falls back to 'fallback_path' if no gravatar is found.
7
+ # default 'fallback_path' is "/images/gravatar_default.jpg".
8
+ # example:
9
+ #
10
+ # class GravatarController < Ramaze::Controller
11
+ # helper :gravatar
12
+ #
13
+ # def index
14
+ # @gravatar_thumbnail_src = gravatar(session[:email] || 'riku@helloit.fi')
15
+ # end
16
+ # end
17
+ #
18
+ # /view/gravatar/index.html:
19
+ # <img src="#{@gravatar_thumbnail_src}" />
20
+
4
21
  def gravatar(email, size = 32, fallback_path = "/images/gravatar_default.jpg")
5
22
  emailhash = Digest::MD5.hexdigest(email)
6
23
 
7
24
  fallback = Request.current.domain
8
25
  fallback.path = fallback_path
9
- default = h(fallback.to_s)
26
+ default = Rack::Utils.escape(fallback.to_s)
10
27
 
11
28
  return "http://www.gravatar.com/avatar.php?gravatar_id=#{emailhash}&default=#{default}&size=#{size}"
12
29
  end
@@ -7,50 +7,77 @@ module Ramaze
7
7
 
8
8
  UUID_GENERATOR = UUID.new
9
9
 
10
- @session_nonce = "authentication_digest_nonce"
10
+ SESSION_NONCE = 'httpdigest_authentication_nonce'
11
+ SESSION_OPAQUE = 'httpdigest_authentication_opaque'
11
12
 
12
13
  def httpdigest_logout
13
- session.delete( @session_nonce )
14
+ session.delete( SESSION_NONCE )
15
+ session.delete( SESSION_OPAQUE )
16
+ end
17
+
18
+ def httpdigest_headers uid, realm
19
+ session[ SESSION_NONCE ] = UUID_GENERATOR.generate
20
+ session[ SESSION_OPAQUE ][ realm ][ uid ] = UUID_GENERATOR.generate
21
+ response['WWW-Authenticate'] =
22
+ %|Digest realm="#{realm}",| +
23
+ %|qop="auth,auth-int",| +
24
+ %|nonce="#{session[SESSION_NONCE]}",| +
25
+ %|opaque="#{session[SESSION_OPAQUE][realm][uid]}"|
14
26
  end
15
27
 
16
28
  def httpdigest(uid, realm)
17
- session_opaque = "authentication_digest_opaque_#{uid}"
29
+ session[ SESSION_OPAQUE ] ||= {}
30
+ session[ SESSION_OPAQUE ][ realm ] ||= {}
18
31
 
19
- session[session_opaque] ||= UUID_GENERATOR.generate
32
+ if request.env['HTTP_AUTHORIZATION']
20
33
 
21
- authorized = false
34
+ authorized = false
22
35
 
23
- if session[@session_nonce] and request.env['HTTP_AUTHORIZATION']
36
+ if session[ SESSION_NONCE ] and session[ SESSION_OPAQUE ][ realm ][ uid ]
24
37
 
25
- auth_split = request.env['HTTP_AUTHORIZATION'].split
26
- authentication_type = auth_split[0]
27
- authorization = Rack::Auth::Digest::Params.parse( auth_split[1..-1].join(' ') )
28
- digest_response, username, nonce, nc, cnonce, qop =
29
- authorization.values_at(*%w[response username nonce nc cnonce qop])
38
+ auth_split = request.env['HTTP_AUTHORIZATION'].split
39
+ authentication_type = auth_split[0]
40
+ authorization = Rack::Auth::Digest::Params.parse( auth_split[1..-1].join(' ') )
30
41
 
31
- if authentication_type == 'Digest'
32
- if nonce == session[@session_nonce]
33
- ha1 = yield(username)
34
- ha2 = MD5.hexdigest("#{request.request_method}:#{request.fullpath}")
35
- md5 = MD5.hexdigest([ha1, nonce, nc, cnonce, qop, ha2].join(':'))
42
+ digest_response, username, nonce, nc, cnonce, qop, opaque =
43
+ authorization.values_at(*%w[response username nonce nc cnonce qop opaque])
36
44
 
37
- authorized = digest_response == md5
45
+ if authentication_type == 'Digest'
46
+ if nonce == session[SESSION_NONCE] and opaque == session[SESSION_OPAQUE][realm][uid]
47
+ h1 = nil
48
+ if respond_to?( :httpdigest_lookup_password )
49
+ ha1 = httpdigest_lookup_password( username )
50
+ else
51
+ if respond_to?( :httpdigest_lookup_plaintext_password )
52
+ ha1 = MD5.hexdigest( "#{username}:#{realm}:#{httpdigest_lookup_plaintext_password( username )}" )
53
+ else
54
+ if block_given?
55
+ ha1 = yield( username )
56
+ else
57
+ raise "No password lookup handler found"
58
+ end
59
+ end
60
+ end
61
+ ha2 = MD5.hexdigest([request.request_method,request.fullpath].join(':'))
62
+ md5 = MD5.hexdigest([ha1, nonce, nc, cnonce, qop, ha2].join(':'))
63
+
64
+ authorized = digest_response == md5
65
+ end
38
66
  end
67
+
39
68
  end
40
- end
41
69
 
42
- unless authorized
43
- session[@session_nonce] = UUID_GENERATOR.generate
44
- response['WWW-Authenticate'] =
45
- %|Digest realm="#{realm}",| +
46
- %|qop="auth,auth-int",| +
47
- %|nonce="#{session[@session_nonce]}",| +
48
- %|opaque="#{session[session_opaque]}"|
49
- if respond_to?( :httpdigest_failure )
50
- httpdigest_failure
51
- else
70
+ unless authorized
71
+ httpdigest_headers( uid, realm )
52
72
  respond('Unauthorized', 401)
53
73
  end
74
+
75
+ else
76
+
77
+ httpdigest_headers( uid, realm )
78
+ httpdigest_failure if respond_to?( :httpdigest_failure )
79
+ respond('Unauthorized', 401)
80
+
54
81
  end
55
82
 
56
83
  authorization["username"]
@@ -15,7 +15,7 @@ module Ramaze
15
15
  def markaby(ivs = {}, helpers = nil, &block)
16
16
  builder = ::Markaby::Builder
17
17
  builder.extend(Ramaze::Helper::Methods)
18
- builder.send(:helper, :link)
18
+ builder.send(:helper, :redirect, :link, :sendfile, :flash, :cgi, :partial)
19
19
 
20
20
  iv_hash = {}
21
21
  instance_variables.each do |iv|
@@ -1,3 +1,5 @@
1
+ require 'maruku'
2
+
1
3
  module Ramaze
2
4
  module Helper::Maruku
3
5
  def maruku(text)
@@ -165,7 +165,7 @@ module Ramaze
165
165
  text = h(text.to_s)
166
166
 
167
167
  params = Ramaze::Request.current.params.merge(@var.to_s => n)
168
- name = Ramaze::Request.current.request_path
168
+ name = Ramaze::Request.current.path_info
169
169
  hash[:href] = R(name, params)
170
170
 
171
171
  g.a(hash){ text }
@@ -69,7 +69,7 @@ module Ramaze
69
69
  else
70
70
  roots = [options[:controller].template_paths].flatten
71
71
 
72
- if (files = Dir["{#{roots.join(',')}}"/"{#{file},#{file}.*}"]).any?
72
+ if (files = Dir[File.join("{#{roots.join(',')}}","{#{file},#{file}.*}")]).any?
73
73
  options[:template] = files.first.squeeze '/'
74
74
  else
75
75
  Log.warn "render_template: #{file} does not exist in the following directories: #{roots.join(',')}."
@@ -59,7 +59,7 @@ module Ramaze
59
59
  def raw_redirect(target, opts = {})
60
60
  target = target.to_s
61
61
  header = {'Location' => target}
62
- status = opts[:status] || STATUS_CODE["Moved Temporarily"]
62
+ status = opts[:status] || 302 # Found
63
63
  body = %{You are being redirected, please follow <a href="#{target}">this link to: #{target}</a>!}
64
64
 
65
65
  Log.info("Redirect to '#{target}'")
@@ -72,10 +72,28 @@ module Ramaze
72
72
  request[:redirected]
73
73
  end
74
74
 
75
- # redirect to the location the browser says it's coming from.
75
+ # Redirect to the location the browser says it's coming from.
76
+ # If the current address is the same as the referrer or no referrer exists
77
+ # yet, we will redirect to +fallback+.
78
+ #
79
+ # NOTE:
80
+ # * In some cases this may result in a double redirect, given that the
81
+ # request query parameters may change order. We don't have a nice way
82
+ # of handling that yet, but it should be very, very rare
83
+
84
+ def redirect_referer(fallback = R(:/))
85
+ if referer = request.referer and url = request.url
86
+ referer_uri = URI(referer)
87
+ request_uri = URI(url)
76
88
 
77
- def redirect_referer
78
- redirect request.referer
89
+ if referer_uri == request_uri
90
+ redirect fallback
91
+ else
92
+ redirect referer
93
+ end
94
+ else
95
+ redirect fallback
96
+ end
79
97
  end
80
98
  alias redirect_referrer redirect_referer
81
99
  end