ramaze 2008.11 → 2009.01

Sign up to get free protection for your applications and to get access to all the features.
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