utopia 0.12.6 → 1.0.0

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 (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