utopia 0.12.6 → 1.0.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (126) hide show
  1. checksums.yaml +4 -4
  2. data/.travis.yml +6 -2
  3. data/Gemfile +6 -0
  4. data/README.md +48 -14
  5. data/Rakefile +5 -0
  6. data/bin/utopia +132 -15
  7. data/lib/utopia.rb +13 -10
  8. data/lib/utopia/content.rb +140 -0
  9. data/lib/utopia/content/link.rb +124 -0
  10. data/lib/utopia/content/links.rb +228 -0
  11. data/lib/utopia/content/node.rb +387 -0
  12. data/lib/utopia/content/processor.rb +128 -0
  13. data/lib/utopia/content/tag.rb +102 -0
  14. data/lib/utopia/controller.rb +137 -0
  15. data/lib/utopia/controller/action.rb +112 -0
  16. data/lib/utopia/controller/base.rb +174 -0
  17. data/lib/utopia/{middleware/controller → controller}/variables.rb +36 -38
  18. data/lib/utopia/exception_handler.rb +79 -0
  19. data/lib/utopia/extensions/array.rb +2 -2
  20. data/lib/utopia/localization.rb +143 -0
  21. data/lib/utopia/mail_exceptions.rb +136 -0
  22. data/lib/utopia/middleware.rb +7 -22
  23. data/lib/utopia/path.rb +150 -60
  24. data/lib/utopia/redirector.rb +152 -0
  25. data/lib/utopia/{extensions/hash.rb → session.rb} +4 -6
  26. data/lib/utopia/session/encrypted_cookie.rb +46 -48
  27. data/lib/utopia/{middleware/directory_index.rb → session/lazy_hash.rb} +44 -27
  28. data/lib/utopia/static.rb +255 -0
  29. data/lib/utopia/tags/deferred.rb +12 -8
  30. data/lib/utopia/tags/environment.rb +18 -6
  31. data/lib/utopia/tags/node.rb +12 -8
  32. data/lib/utopia/tags/override.rb +12 -12
  33. data/lib/utopia/version.rb +1 -1
  34. data/setup/.bowerrc +3 -0
  35. data/{lib/utopia/setup → setup}/Gemfile +1 -1
  36. data/setup/Rakefile +4 -0
  37. data/{lib/utopia/setup → setup}/cache/head/readme.txt +0 -0
  38. data/{lib/utopia/setup → setup}/cache/meta/readme.txt +0 -0
  39. data/setup/config.ru +64 -0
  40. data/{lib/utopia/setup → setup}/lib/readme.txt +0 -0
  41. data/{lib/utopia/setup → setup}/pages/_heading.xnode +0 -0
  42. data/{lib/utopia/setup → setup}/pages/_page.xnode +1 -1
  43. data/{lib/utopia/setup → setup}/pages/_static/icon.png +0 -0
  44. data/setup/pages/_static/site.css +70 -0
  45. data/{lib/utopia/setup → setup}/pages/errors/exception.xnode +0 -0
  46. data/{lib/utopia/setup → setup}/pages/errors/file-not-found.xnode +0 -0
  47. data/{lib/utopia/setup → setup}/pages/links.yaml +0 -0
  48. data/setup/pages/welcome/index.xnode +17 -0
  49. data/{lib/utopia/setup → setup}/public/readme.txt +0 -0
  50. data/spec/utopia/content/link_spec.rb +108 -0
  51. data/spec/utopia/content/links/foo/index.xnode +0 -0
  52. data/spec/utopia/content/links/foo/links.yaml +2 -0
  53. data/spec/utopia/content/links/foo/test.de.xnode +0 -0
  54. data/spec/utopia/content/links/foo/test.en.xnode +0 -0
  55. data/spec/utopia/content/links/links.yaml +9 -0
  56. data/spec/utopia/content/links/welcome.xnode +0 -0
  57. data/spec/utopia/content/localized/five/index.en.xnode +0 -0
  58. data/spec/utopia/content/localized/four/index.en.xnode +0 -0
  59. data/spec/utopia/content/localized/four/index.zh.xnode +0 -0
  60. data/spec/utopia/content/localized/four/links.yaml +4 -0
  61. data/spec/utopia/content/localized/links.yaml +16 -0
  62. data/spec/utopia/content/localized/one.xnode +0 -0
  63. data/spec/utopia/content/localized/three/index.xnode +0 -0
  64. data/spec/utopia/content/localized/two.en.xnode +0 -0
  65. data/spec/utopia/content/localized/two.zh.xnode +0 -0
  66. data/spec/utopia/content/node/ordered/first.xnode +0 -0
  67. data/spec/utopia/content/node/ordered/index.xnode +0 -0
  68. data/spec/utopia/content/node/ordered/links.yaml +4 -0
  69. data/spec/utopia/content/node/ordered/second.xnode +0 -0
  70. data/spec/utopia/content/node/related/foo.en.xnode +0 -0
  71. data/spec/utopia/content/node/related/foo.ja.xnode +0 -0
  72. data/spec/utopia/content/node/related/links.yaml +4 -0
  73. data/spec/utopia/content/node_spec.rb +63 -0
  74. data/spec/utopia/{middleware/content_spec.rb → content/processor_spec.rb} +34 -23
  75. data/spec/utopia/content_spec.rb +87 -0
  76. data/spec/utopia/content_spec.ru +10 -0
  77. data/spec/utopia/{middleware/controller_spec.rb → controller_spec.rb} +61 -16
  78. data/spec/utopia/controller_spec.ru +4 -0
  79. data/spec/utopia/extensions_spec.rb +6 -17
  80. data/spec/utopia/localization_spec.rb +60 -0
  81. data/spec/utopia/localization_spec.ru +11 -0
  82. data/{lib/utopia/tags.rb → spec/utopia/middleware_spec.rb} +8 -14
  83. data/spec/utopia/{middleware/content_root → pages}/_heading.xnode +0 -0
  84. data/spec/utopia/pages/content/_show-value.xnode +1 -0
  85. data/spec/utopia/pages/content/test-partial.xnode +1 -0
  86. data/spec/utopia/pages/controller/controller.rb +28 -0
  87. data/spec/utopia/pages/controller/index.xnode +1 -0
  88. data/spec/utopia/pages/controller/nested/controller.rb +4 -0
  89. data/spec/utopia/{middleware/content_root → pages}/index.xnode +0 -0
  90. data/spec/utopia/pages/localized.de.txt +1 -0
  91. data/spec/utopia/pages/localized.en.txt +1 -0
  92. data/spec/utopia/pages/localized.jp.txt +1 -0
  93. data/spec/utopia/pages/node/index.xnode +1 -0
  94. data/spec/utopia/pages/test.txt +1 -0
  95. data/spec/utopia/path_spec.rb +109 -0
  96. data/spec/utopia/rack_spec.rb +2 -0
  97. data/spec/utopia/session_spec.rb +82 -0
  98. data/spec/utopia/session_spec.ru +20 -0
  99. data/spec/utopia/spec_helper.rb +16 -0
  100. data/{lib/utopia/extensions/string.rb → spec/utopia/static_spec.rb} +24 -15
  101. data/spec/utopia/static_spec.ru +4 -0
  102. data/utopia.gemspec +3 -3
  103. metadata +138 -54
  104. data/lib/utopia/extensions/regexp.rb +0 -33
  105. data/lib/utopia/link.rb +0 -288
  106. data/lib/utopia/middleware/all.rb +0 -33
  107. data/lib/utopia/middleware/content.rb +0 -157
  108. data/lib/utopia/middleware/content/node.rb +0 -386
  109. data/lib/utopia/middleware/content/processor.rb +0 -123
  110. data/lib/utopia/middleware/controller.rb +0 -130
  111. data/lib/utopia/middleware/controller/action.rb +0 -121
  112. data/lib/utopia/middleware/controller/base.rb +0 -184
  113. data/lib/utopia/middleware/exception_handler.rb +0 -80
  114. data/lib/utopia/middleware/localization.rb +0 -147
  115. data/lib/utopia/middleware/localization/name.rb +0 -69
  116. data/lib/utopia/middleware/mail_exceptions.rb +0 -138
  117. data/lib/utopia/middleware/redirector.rb +0 -146
  118. data/lib/utopia/middleware/requester.rb +0 -126
  119. data/lib/utopia/middleware/static.rb +0 -295
  120. data/lib/utopia/setup.rb +0 -60
  121. data/lib/utopia/setup/config.ru +0 -47
  122. data/lib/utopia/setup/pages/_static/background.png +0 -0
  123. data/lib/utopia/setup/pages/_static/site.css +0 -48
  124. data/lib/utopia/setup/pages/welcome/index.xnode +0 -7
  125. data/lib/utopia/tag.rb +0 -105
  126. data/lib/utopia/tags/all.rb +0 -34
@@ -1,80 +0,0 @@
1
- # Copyright, 2014, by Samuel G. D. Williams. <http://www.codeotaku.com>
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
11
- # all 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
19
- # THE SOFTWARE.
20
-
21
- require 'utopia/middleware'
22
- require 'utopia/extensions/string'
23
-
24
- module Utopia
25
- module Middleware
26
- class ExceptionHandler
27
- def initialize(app, location)
28
- @app = app
29
-
30
- @location = location
31
- end
32
-
33
- def fatal_error(env, ex)
34
- body = StringIO.new
35
-
36
- body.puts "<!DOCTYPE html><html><head><title>Fatal Error</title></head><body>"
37
- body.puts "<h1>Fatal Error</h1>"
38
- body.puts "<p>While requesting resource #{env['PATH_INFO'].to_html}, a fatal error occurred.</p>"
39
- body.puts "<blockquote><strong>#{ex.class.name.to_html}</strong>: #{ex.to_s.to_html}</blockquote>"
40
- body.puts "<p>There is nothing more we can do to fix the problem at this point.</p>"
41
- body.puts "<p>We apologize for the inconvenience.</p>"
42
- body.puts "</body></html>"
43
- body.rewind
44
-
45
- return [400, {"Content-Type" => "text/html"}, body]
46
- end
47
-
48
- def redirect(env, ex)
49
- return @app.call(env.merge('PATH_INFO' => @location, 'REQUEST_METHOD' => 'GET'))
50
- end
51
-
52
- def call(env)
53
- begin
54
- return @app.call(env)
55
- rescue Exception => ex
56
- log = ::Logger.new(env['rack.errors'])
57
-
58
- log.error "Exception #{ex.to_s.dump}!"
59
-
60
- ex.backtrace.each do |bt|
61
- log.error bt
62
- end
63
-
64
- if env['PATH_INFO'] == @location
65
- return fatal_error(env, ex)
66
- else
67
-
68
- # If redirection fails
69
- begin
70
- return redirect(env, ex)
71
- rescue
72
- return fatal_error(env, ex)
73
- end
74
-
75
- end
76
- end
77
- end
78
- end
79
- end
80
- end
@@ -1,147 +0,0 @@
1
- # Copyright, 2012, by Samuel G. D. Williams. <http://www.codeotaku.com>
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
11
- # all 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
19
- # THE SOFTWARE.
20
-
21
- require 'utopia/middleware'
22
- require 'utopia/middleware/localization/name'
23
-
24
- module Rack
25
- class Request
26
- def current_locale
27
- env["utopia.current_locale"]
28
- end
29
-
30
- def all_locales
31
- localization.all_locales
32
- end
33
-
34
- def localization
35
- env["utopia.localization"]
36
- end
37
- end
38
- end
39
-
40
- module Utopia
41
- module Middleware
42
-
43
- class Localization
44
- def initialize(app, options = {})
45
- @app = app
46
-
47
- @default_locale = options[:default] || "en"
48
- @all_locales = options[:all] || ["en"]
49
-
50
- @nonlocalized = options[:nonlocalized] || []
51
- end
52
-
53
- def named_locale(resource_name)
54
- if resource_name
55
- Name.extract_locale(resource_name, @all_locales)
56
- else
57
- nil
58
- end
59
- end
60
-
61
- attr :all_locales
62
- attr :default_locale
63
-
64
- def check_resource(resource_name, resource_locale, env)
65
- localized_name = Name.localized(resource_name, resource_locale, @all_locales).join(".")
66
- localized_path = Path.create(env["PATH_INFO"]).dirname + localized_name
67
-
68
- localization_probe = env.dup
69
- localization_probe["REQUEST_METHOD"] = "HEAD"
70
- localization_probe["PATH_INFO"] = localized_path.to_s
71
-
72
- # Find out if a resource exists for the requested localization
73
- return [localized_path, @app.call(localization_probe)]
74
- end
75
-
76
- def nonlocalized?(env)
77
- @nonlocalized.each do |pattern|
78
- case pattern
79
- when String
80
- return true if pattern == env["PATH_INFO"]
81
- when Regexp
82
- return true if pattern.match(env["PATH_INFO"])
83
- when pattern.respond_to?(:call)
84
- return true if pattern.call(env)
85
- end
86
- end
87
-
88
- return false
89
- end
90
-
91
- def call(env)
92
- # Check for a non-localized resource.
93
- if nonlocalized?(env)
94
- return @app.call(env)
95
- end
96
-
97
- # Otherwise, we need to check if the resource has been localized based on the request and referer parameters.
98
- path = Path.create(env["PATH_INFO"])
99
- env["utopia.localization"] = self
100
-
101
- referer_locale = named_locale(env['HTTP_REFERER'])
102
- request_locale = named_locale(path.basename)
103
- resource_name = Name.nonlocalized(path.basename, @all_locales).join(".")
104
-
105
- response = nil
106
- if request_locale
107
- env["utopia.current_locale"] = request_locale
108
- resource_path, response = check_resource(resource_name, request_locale, env)
109
- elsif referer_locale
110
- env["utopia.current_locale"] = referer_locale
111
- resource_path, response = check_resource(resource_name, referer_locale, env)
112
- end
113
-
114
- # If the previous checks failed, i.e. there was no request/referer locale
115
- # or the response was 404 (i.e. no localised resource), we check for the
116
- # @default_locale
117
- if response == nil || response[0] >= 400
118
- env["utopia.current_locale"] = @default_locale
119
- resource_path, response = check_resource(resource_name, @default_locale, env)
120
- end
121
-
122
- # If the response is 2xx, we have a localised resource
123
- if response[0] < 300
124
- # If the original request was the same as the localized request,
125
- if path.basename == resource_path.basename
126
- # The resource URI is correct.
127
- return @app.call(env)
128
- else
129
- # Redirect to the correct resource URI.
130
- return [307, {"Location" => resource_path.to_s}, []]
131
- end
132
- elsif response[0] < 400
133
- # We have encountered a redirect while accessing the localized resource
134
- return response
135
- else
136
- # A localized resource was not found, return the unlocalised resource path,
137
- if path.basename == resource_name
138
- return @app.call(env)
139
- else
140
- return [307, {"Location" => (path.dirname + resource_name).to_s}, []]
141
- end
142
- end
143
- end
144
- end
145
-
146
- end
147
- end
@@ -1,69 +0,0 @@
1
- # Copyright, 2012, by Samuel G. D. Williams. <http://www.codeotaku.com>
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
11
- # all 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
19
- # THE SOFTWARE.
20
-
21
- module Utopia
22
- module Middleware
23
- class Localization
24
-
25
- module Name
26
- def self.nonlocalized(resource_name, all_locales)
27
- resource_name = resource_name.split(".") unless resource_name.kind_of? Array
28
-
29
- # We either have a file extension or an existing locale
30
- if all_locales.include?(resource_name[-1])
31
- resource_name.delete_at(-1)
32
- elsif all_locales.include?(resource_name[-2])
33
- resource_name.delete_at(-2)
34
- end
35
-
36
- resource_name
37
- end
38
-
39
- def self.extract_locale(resource_name, all_locales)
40
- resource_name = resource_name.split(".") unless resource_name.kind_of? Array
41
-
42
- # We either have a file extension or an existing locale
43
- if all_locales.include?(resource_name[-1])
44
- return resource_name[-1]
45
- elsif all_locales.include?(resource_name[-2])
46
- return resource_name[-2]
47
- end
48
-
49
- return nil
50
- end
51
-
52
- def self.localized(resource_name, locale, all_locales)
53
- nonlocalized_name = nonlocalized(resource_name, all_locales)
54
-
55
- if locale == nil
56
- return nonlocalized_name
57
- end
58
-
59
- if nonlocalized_name.size == 1
60
- return nonlocalized_name.push(locale)
61
- else
62
- return nonlocalized_name.insert(-2, locale)
63
- end
64
- end
65
- end
66
-
67
- end
68
- end
69
- end
@@ -1,138 +0,0 @@
1
- # Copyright, 2013, by Samuel G. D. Williams. <http://www.codeotaku.com>
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
11
- # all 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
19
- # THE SOFTWARE.
20
-
21
- require 'net/smtp'
22
- require 'mail'
23
-
24
- module Utopia
25
- module Middleware
26
- # Catches all exceptions raised from the app it wraps and sends a useful email with the exception, stacktrace, and contents of the environment.
27
- class MailExceptions
28
- # A basic local non-authenticated SMTP server.
29
- LOCAL_SMTP = [:smtp, {
30
- :address => "localhost",
31
- :port => 25,
32
- :enable_starttls_auto => false
33
- }]
34
-
35
- def initialize(app, config = {})
36
- @app = app
37
-
38
- @to = config[:to] || "postmaster"
39
- @from = config.fetch(:from) {(ENV['USER'] || 'rack') + "@localhost"}
40
- @subject = config[:subject] || '%{exception} [PID %{pid} : %{cwd}]'
41
- @delivery_method = config.fetch(:delivery_method, LOCAL_SMTP)
42
-
43
- @dump_environment = config.fetch(:dump_environment, false)
44
- end
45
-
46
- def call(env)
47
- begin
48
- return @app.call(env)
49
- rescue => exception
50
- send_notification exception, env
51
-
52
- raise
53
- end
54
- end
55
-
56
- private
57
-
58
- REQUEST_KEYS = [:ip, :referrer, :path, :user_agent]
59
-
60
- def generate_body(exception, env)
61
- io = StringIO.new
62
-
63
- # Dump out useful rack environment variables:
64
- request = Rack::Request.new(env)
65
-
66
- io.puts "#{request.request_method} #{request.url}"
67
-
68
- io.puts
69
-
70
- REQUEST_KEYS.each do |key|
71
- value = request.send(key)
72
- io.puts "request.#{key}: #{value.inspect}"
73
- end
74
-
75
- request.params.each do |key, value|
76
- io.puts "request.params.#{key}: #{value.inspect}"
77
- end
78
-
79
- io.puts
80
-
81
- io.puts "#{exception.class.name}: #{exception.to_s}"
82
-
83
- if exception.respond_to?(:backtrace)
84
- io.puts exception.backtrace
85
- else
86
- io.puts exception.to_s
87
- end
88
-
89
- return io.string
90
- end
91
-
92
- def generate_mail(exception, env)
93
- attributes = {
94
- exception: exception.class.name,
95
- pid: $$,
96
- cwd: Dir.getwd,
97
- }
98
-
99
- mail = Mail.new(
100
- :from => @from,
101
- :to => @to,
102
- :subject => @subject % attributes
103
- )
104
-
105
- mail.text_part = Mail::Part.new
106
- mail.text_part.body = generate_body(exception, env)
107
-
108
- if body = extract_body(env) and body.size > 0
109
- mail.attachments['body.bin'] = body
110
- end
111
-
112
- if @dump_environment
113
- mail.attachments['environment.yaml'] = YAML::dump(env)
114
- end
115
-
116
- return mail
117
- end
118
-
119
- def send_notification(exception, env)
120
- mail = generate_mail(exception, env)
121
-
122
- mail.delivery_method(*@delivery_method) if @delivery_method
123
-
124
- mail.deliver
125
- rescue => mail_exception
126
- $stderr.puts mail_exception.to_s
127
- $stderr.puts mail_exception.backtrace
128
- end
129
-
130
- def extract_body(env)
131
- if io = env['rack.input']
132
- io.rewind if io.respond_to?(:rewind)
133
- io.read
134
- end
135
- end
136
- end
137
- end
138
- end
@@ -1,146 +0,0 @@
1
- # Copyright, 2012, by Samuel G. D. Williams. <http://www.codeotaku.com>
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
11
- # all 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
19
- # THE SOFTWARE.
20
-
21
- require 'utopia/middleware'
22
- require 'utopia/extensions/regexp'
23
- require 'utopia/extensions/string'
24
-
25
- module Utopia
26
- module Middleware
27
-
28
- class FailedRequestError < StandardError
29
- def initialize(resource_path, resource_status, error_path, error_status)
30
- @resource_path = resource_path
31
- @resource_status = resource_status
32
-
33
- @error_path = error_path
34
- @error_status = error_status
35
- end
36
-
37
- def to_s
38
- "Requested resource #{@resource_path} resulted in a #{@resource_status} error. Requested error handler #{@error_path} resulted in a #{@error_status} error."
39
- end
40
- end
41
-
42
- class Redirector
43
- # Redirects a whole source tree to a destination tree, given by the roots.
44
- def self.moved(source_root, destination_root)
45
- return [
46
- /^#{Regexp.escape(source_root)}(.*)$/,
47
- lambda do |match|
48
- [301, {"Location" => (destination_root + match[1]).to_s}, []]
49
- end
50
- ]
51
- end
52
-
53
- private
54
- def normalize_strings(strings)
55
- normalized = {}
56
-
57
- strings.each_pair do |key, value|
58
- if Array === key
59
- key.each { |s| normalized[s] = value }
60
- else
61
- normalized[key] = value
62
- end
63
- end
64
-
65
- return normalized
66
- end
67
-
68
- def normalize_patterns(patterns)
69
- normalized = []
70
-
71
- patterns.each do |pattern|
72
- uri = pattern.pop
73
-
74
- pattern.each do |key|
75
- normalized.push([key, uri])
76
- end
77
- end
78
-
79
- return normalized
80
- end
81
-
82
- public
83
- def initialize(app, options = {})
84
- @app = app
85
-
86
- @strings = options[:strings] || {}
87
- @patterns = options[:patterns] || []
88
-
89
- @patterns.collect! do |rule|
90
- if Symbol === rule[0]
91
- self.class.send(*rule)
92
- else
93
- rule
94
- end
95
- end
96
-
97
- @strings = normalize_strings(@strings)
98
- @patterns = normalize_patterns(@patterns)
99
-
100
- @errors = options[:errors]
101
- end
102
-
103
- def redirect(uri, match_data)
104
- if uri.respond_to? :call
105
- return uri.call(match_data)
106
- else
107
- return [301, {"Location" => uri.to_s}, []]
108
- end
109
- end
110
-
111
- def call(env)
112
- base_path = env['PATH_INFO']
113
-
114
- if uri = @strings[base_path]
115
- return redirect(@strings[base_path], base_path)
116
- end
117
-
118
- @patterns.each do |pattern, uri|
119
- if match_data = pattern.match(base_path)
120
- result = redirect(uri, match_data)
121
-
122
- return result if result != nil
123
- end
124
- end
125
-
126
- response = @app.call(env)
127
-
128
- if @errors && response[0] >= 400 && uri = @errors[response[0]]
129
- error_request = env.merge("PATH_INFO" => uri, "REQUEST_METHOD" => "GET")
130
- error_response = @app.call(error_request)
131
-
132
- if error_response[0] >= 400
133
- raise FailedRequestError.new(env['PATH_INFO'], response[0], uri, error_response[0])
134
- else
135
- # Feed the error code back with the error document
136
- error_response[0] = response[0]
137
- return error_response
138
- end
139
- else
140
- return response
141
- end
142
- end
143
- end
144
-
145
- end
146
- end