utopia 0.9.17

Sign up to get free protection for your applications and to get access to all the features.
Files changed (37) hide show
  1. data/ext/utopia/xnode/fast_scanner/extconf.rb +6 -0
  2. data/ext/utopia/xnode/fast_scanner/parser.c +289 -0
  3. data/lib/utopia.rb +2 -0
  4. data/lib/utopia/etanni.rb +96 -0
  5. data/lib/utopia/extensions.rb +87 -0
  6. data/lib/utopia/link.rb +243 -0
  7. data/lib/utopia/middleware.rb +33 -0
  8. data/lib/utopia/middleware/all.rb +24 -0
  9. data/lib/utopia/middleware/benchmark.rb +47 -0
  10. data/lib/utopia/middleware/content.rb +139 -0
  11. data/lib/utopia/middleware/content/node.rb +363 -0
  12. data/lib/utopia/middleware/controller.rb +198 -0
  13. data/lib/utopia/middleware/directory_index.rb +54 -0
  14. data/lib/utopia/middleware/localization.rb +94 -0
  15. data/lib/utopia/middleware/localization/name.rb +64 -0
  16. data/lib/utopia/middleware/logger.rb +68 -0
  17. data/lib/utopia/middleware/redirector.rb +171 -0
  18. data/lib/utopia/middleware/requester.rb +116 -0
  19. data/lib/utopia/middleware/static.rb +186 -0
  20. data/lib/utopia/path.rb +193 -0
  21. data/lib/utopia/response_helper.rb +22 -0
  22. data/lib/utopia/session/encrypted_cookie.rb +115 -0
  23. data/lib/utopia/tag.rb +84 -0
  24. data/lib/utopia/tags.rb +32 -0
  25. data/lib/utopia/tags/all.rb +25 -0
  26. data/lib/utopia/tags/env.rb +24 -0
  27. data/lib/utopia/tags/fortune.rb +20 -0
  28. data/lib/utopia/tags/gallery.rb +175 -0
  29. data/lib/utopia/tags/google_analytics.rb +37 -0
  30. data/lib/utopia/tags/node.rb +24 -0
  31. data/lib/utopia/tags/override.rb +28 -0
  32. data/lib/utopia/time_store.rb +102 -0
  33. data/lib/utopia/version.rb +24 -0
  34. data/lib/utopia/xnode.rb +17 -0
  35. data/lib/utopia/xnode/processor.rb +97 -0
  36. data/lib/utopia/xnode/scanner.rb +153 -0
  37. metadata +168 -0
@@ -0,0 +1,54 @@
1
+ # Copyright (c) 2010 Samuel Williams. Released under the GNU GPLv3.
2
+ #
3
+ # This program is free software: you can redistribute it and/or modify
4
+ # it under the terms of the GNU General Public License as published by
5
+ # the Free Software Foundation, either version 3 of the License, or
6
+ # (at your option) any later version.
7
+ #
8
+ # This program is distributed in the hope that it will be useful,
9
+ # but WITHOUT ANY WARRANTY; without even the implied warranty of
10
+ # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
11
+ # GNU General Public License for more details.
12
+ #
13
+ # You should have received a copy of the GNU General Public License
14
+ # along with this program. If not, see <http://www.gnu.org/licenses/>.
15
+
16
+ require 'utopia/middleware'
17
+ require 'utopia/path'
18
+
19
+ module Utopia
20
+ module Middleware
21
+
22
+ class DirectoryIndex
23
+ def initialize(app, options = {})
24
+ @app = app
25
+ @root = options[:root] || Utopia::Middleware::default_root
26
+
27
+ @files = ["index.html"]
28
+
29
+ @default = "index"
30
+ end
31
+
32
+ def call(env)
33
+ path = Path.create(env["PATH_INFO"])
34
+
35
+ if path.directory?
36
+ # Check to see if one of the files exists in the requested directory
37
+ @files.each do |file|
38
+ if File.exist?(File.join(@root, path.components, file))
39
+ env["PATH_INFO"] = (path + file).to_s
40
+ return @app.call(env)
41
+ end
42
+ end
43
+
44
+ # Use the default path
45
+ env["PATH_INFO"] = (path + @default).to_s
46
+ return @app.call(env)
47
+ else
48
+ return @app.call(env)
49
+ end
50
+ end
51
+ end
52
+
53
+ end
54
+ end
@@ -0,0 +1,94 @@
1
+ # Copyright (c) 2010 Samuel Williams. Released under the GNU GPLv3.
2
+ #
3
+ # This program is free software: you can redistribute it and/or modify
4
+ # it under the terms of the GNU General Public License as published by
5
+ # the Free Software Foundation, either version 3 of the License, or
6
+ # (at your option) any later version.
7
+ #
8
+ # This program is distributed in the hope that it will be useful,
9
+ # but WITHOUT ANY WARRANTY; without even the implied warranty of
10
+ # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
11
+ # GNU General Public License for more details.
12
+ #
13
+ # You should have received a copy of the GNU General Public License
14
+ # along with this program. If not, see <http://www.gnu.org/licenses/>.
15
+
16
+ require 'utopia/middleware'
17
+ require 'utopia/response_helper'
18
+ require 'utopia/middleware/localization/name'
19
+
20
+ module Rack
21
+ class Request
22
+ def current_locale
23
+ env["utopia.current_locale"]
24
+ end
25
+
26
+ def all_locales
27
+ localization.all_locales
28
+ end
29
+
30
+ def localization
31
+ env["utopia.localization"]
32
+ end
33
+ end
34
+ end
35
+
36
+ module Utopia
37
+ module Middleware
38
+
39
+ class Localization
40
+ def initialize(app, options = {})
41
+ @app = app
42
+
43
+ @default_locale = options[:default] || "en"
44
+ @all_locales = options[:all] || ["en"]
45
+ end
46
+
47
+ def named_locale(resource_name)
48
+ Name.extract_locale(resource_name, @all_locales)
49
+ end
50
+
51
+ def current_locale(env, resource_name)
52
+ Rack::Request.new(env).GET["locale"] || named_locale(resource_name) || @default_locale
53
+ end
54
+
55
+ attr :all_locales
56
+ attr :default_locale
57
+
58
+ def call(env)
59
+ path = Path.create(env["PATH_INFO"])
60
+
61
+ request_locale = current_locale(env, path.basename)
62
+ resource_name = Name.nonlocalized(path.basename, @all_locales).join(".")
63
+
64
+ env["utopia.current_locale"] = request_locale
65
+ env["utopia.localization"] = self
66
+
67
+ localized_name = Name.localized(resource_name, request_locale, @all_locales).join(".")
68
+
69
+ localization_probe = env.dup
70
+ localization_probe["REQUEST_METHOD"] = "HEAD"
71
+ localization_probe["PATH_INFO"] = (path.dirname + localized_name).to_s
72
+
73
+ response = @app.call(localization_probe)
74
+
75
+ if response[0] < 300
76
+ if path.basename == localized_name
77
+ return @app.call(env)
78
+ else
79
+ return [307, {"Location" => localization_probe["PATH_INFO"]}, []]
80
+ end
81
+ elsif response[0] < 400
82
+ return response
83
+ else
84
+ if path.basename == resource_name
85
+ return @app.call(env)
86
+ else
87
+ return [307, {"Location" => (path.dirname + resource_name).to_s}, []]
88
+ end
89
+ end
90
+ end
91
+ end
92
+
93
+ end
94
+ end
@@ -0,0 +1,64 @@
1
+ # Copyright (c) 2010 Samuel Williams. Released under the GNU GPLv3.
2
+ #
3
+ # This program is free software: you can redistribute it and/or modify
4
+ # it under the terms of the GNU General Public License as published by
5
+ # the Free Software Foundation, either version 3 of the License, or
6
+ # (at your option) any later version.
7
+ #
8
+ # This program is distributed in the hope that it will be useful,
9
+ # but WITHOUT ANY WARRANTY; without even the implied warranty of
10
+ # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
11
+ # GNU General Public License for more details.
12
+ #
13
+ # You should have received a copy of the GNU General Public License
14
+ # along with this program. If not, see <http://www.gnu.org/licenses/>.
15
+
16
+ module Utopia
17
+ module Middleware
18
+ class Localization
19
+
20
+ module Name
21
+ def self.nonlocalized(resource_name, all_locales)
22
+ resource_name = resource_name.split(".") unless resource_name.kind_of? Array
23
+
24
+ # We either have a file extension or an existing locale
25
+ if all_locales.include?(resource_name[-1])
26
+ resource_name.delete_at(-1)
27
+ elsif all_locales.include?(resource_name[-2])
28
+ resource_name.delete_at(-2)
29
+ end
30
+
31
+ resource_name
32
+ end
33
+
34
+ def self.extract_locale(resource_name, all_locales)
35
+ resource_name = resource_name.split(".") unless resource_name.kind_of? Array
36
+
37
+ # We either have a file extension or an existing locale
38
+ if all_locales.include?(resource_name[-1])
39
+ return resource_name[-1]
40
+ elsif all_locales.include?(resource_name[-2])
41
+ return resource_name[-2]
42
+ end
43
+
44
+ return nil
45
+ end
46
+
47
+ def self.localized(resource_name, locale, all_locales)
48
+ nonlocalized_name = nonlocalized(resource_name, all_locales)
49
+
50
+ if locale == nil
51
+ return nonlocalized_name
52
+ end
53
+
54
+ if nonlocalized_name.size == 1
55
+ return nonlocalized_name.push(locale)
56
+ else
57
+ return nonlocalized_name.insert(-2, locale)
58
+ end
59
+ end
60
+ end
61
+
62
+ end
63
+ end
64
+ end
@@ -0,0 +1,68 @@
1
+ # Copyright (c) 2010 Samuel Williams. Released under the GNU GPLv3.
2
+ #
3
+ # This program is free software: you can redistribute it and/or modify
4
+ # it under the terms of the GNU General Public License as published by
5
+ # the Free Software Foundation, either version 3 of the License, or
6
+ # (at your option) any later version.
7
+ #
8
+ # This program is distributed in the hope that it will be useful,
9
+ # but WITHOUT ANY WARRANTY; without even the implied warranty of
10
+ # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
11
+ # GNU General Public License for more details.
12
+ #
13
+ # You should have received a copy of the GNU General Public License
14
+ # along with this program. If not, see <http://www.gnu.org/licenses/>.
15
+
16
+ require 'utopia/middleware'
17
+ require 'utopia/time_store'
18
+
19
+ module Utopia
20
+ module Middleware
21
+
22
+ class Logger
23
+ ACCESS_LOG = "access_log"
24
+ HEADER = [:ip, :agent, :method, :url, :status, :location, :length]
25
+
26
+ def write_log(env, response)
27
+ request = Rack::Request.new(env)
28
+
29
+ record = {
30
+ :ip => request.ip,
31
+ :host => request.host,
32
+ :url => request.url,
33
+ :agent => env['HTTP_USER_AGENT'],
34
+ :status => response[0],
35
+ :method => request.request_method,
36
+ :user => env['REMOTE_USER'],
37
+ :version => env['HTTP_VERSION']
38
+ }
39
+
40
+ if response[1].key? "Location"
41
+ record[:location] = response[1]["Location"]
42
+ end
43
+
44
+ if response[1].key? "Content-Length"
45
+ record[:length] = response[1]["Content-Length"]
46
+ end
47
+
48
+ @log << record
49
+ end
50
+
51
+ def initialize(app, options = {})
52
+ @app = app
53
+
54
+ @log = options[:log] || TimeStore.new(options[:path] || ACCESS_LOG, options[:header] || HEADER)
55
+ end
56
+
57
+ def call(env)
58
+ response = @app.call(env)
59
+
60
+ Thread.new do
61
+ write_log(env, response)
62
+ end
63
+
64
+ return response
65
+ end
66
+ end
67
+ end
68
+ end
@@ -0,0 +1,171 @@
1
+ # Copyright (c) 2010 Samuel Williams. Released under the GNU GPLv3.
2
+ #
3
+ # This program is free software: you can redistribute it and/or modify
4
+ # it under the terms of the GNU General Public License as published by
5
+ # the Free Software Foundation, either version 3 of the License, or
6
+ # (at your option) any later version.
7
+ #
8
+ # This program is distributed in the hope that it will be useful,
9
+ # but WITHOUT ANY WARRANTY; without even the implied warranty of
10
+ # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
11
+ # GNU General Public License for more details.
12
+ #
13
+ # You should have received a copy of the GNU General Public License
14
+ # along with this program. If not, see <http://www.gnu.org/licenses/>.
15
+
16
+ require 'utopia/middleware'
17
+
18
+ module Utopia
19
+ module Middleware
20
+
21
+ class FailedRequestError < StandardError
22
+ def initialize(resource_path, resource_status, error_path, error_status)
23
+ @resource_path = resource_path
24
+ @resource_status = resource_status
25
+
26
+ @error_path = error_path
27
+ @error_status = error_status
28
+ end
29
+
30
+ def to_s
31
+ "Requested resource #{@resource_path} resulted in a #{@resource_status} error. Requested error handler #{@error_path} resulted in a #{@error_status} error."
32
+ end
33
+ end
34
+
35
+ class ExceptionHandler
36
+ def initialize(app, location)
37
+ @app = app
38
+
39
+ @location = location
40
+ end
41
+
42
+ def fatal_error(env, ex)
43
+ body = StringIO.new
44
+
45
+ body.puts "<!DOCTYPE html><html><head><title>Fatal Error</title></head><body>"
46
+
47
+ body.puts "<h1>Fatal Error</h1>"
48
+
49
+ body.puts "<p>While requesting resource #{env['PATH_INFO'].to_html}, a fatal error occurred.</p>"
50
+
51
+ body.puts "<blockquote><strong>#{ex.class.name.to_html}</strong>: #{ex.to_s.to_html}</blockquote>"
52
+
53
+ body.puts "<p>There is nothing more we can do to fix the problem at this point.</p>"
54
+
55
+ body.puts "<p>We apologize for the inconvenience.</p>"
56
+
57
+ body.puts "</body></html>"
58
+ body.rewind
59
+
60
+ return [400, {"Content-Type" => "text/html"}, body]
61
+ end
62
+
63
+ def redirect(env, ex)
64
+ return @app.call(env.merge('PATH_INFO' => @location, 'REQUEST_METHOD' => 'GET'))
65
+ end
66
+
67
+ def call(env)
68
+ begin
69
+ return @app.call(env)
70
+ rescue Exception => ex
71
+ if env['PATH_INFO'] == @location
72
+ return fatal_error(env, ex)
73
+ else
74
+
75
+ # If redirection fails
76
+ begin
77
+ return redirect(env, ex)
78
+ rescue
79
+ return fatal_error(env, ex)
80
+ end
81
+
82
+ end
83
+ end
84
+ end
85
+ end
86
+
87
+ # Legacy Support
88
+ ExceptionRedirector = ExceptionHandler
89
+
90
+ class Redirector
91
+ private
92
+ def normalize_keys(redirects)
93
+ redirects.each do |key, value|
94
+ result = nil
95
+
96
+ case value
97
+ when String
98
+ result = @strings
99
+ when Regexp
100
+ result = @patterns
101
+ else
102
+ $stderr.puts "Warning, could not process redirect #{key.inspect} to #{value.inspect}!"
103
+ next
104
+ end
105
+
106
+ if key.kind_of? Array
107
+ key.each do |subkey|
108
+ result[subkey] = value
109
+ end
110
+ else
111
+ result[key] = value
112
+ end
113
+ end
114
+ end
115
+
116
+ public
117
+ def initialize(app, options = {})
118
+ @app = app
119
+
120
+ @strings = {}
121
+ @patterns = {}
122
+
123
+ normalize_keys(options[:redirects]) if options[:redirects]
124
+
125
+ @errors = options[:errors]
126
+
127
+ LOG.info "#{self.class.name}: Running with #{@strings.size + @patterns.size} rules"
128
+ end
129
+
130
+ def redirect(uri, match_data)
131
+ if uri.respond_to? :call
132
+ return uri.call(match_data)
133
+ else
134
+ return [301, {"Location" => uri.to_s}, []]
135
+ end
136
+ end
137
+
138
+ def call(env)
139
+ base_path = env['PATH_INFO']
140
+
141
+ if uri = @strings[base_path]
142
+ return redirect(@strings[base_path], base_path)
143
+ end
144
+
145
+ @patterns.each do |pattern, uri|
146
+ if match_data = base_path.match(pattern)
147
+ return redirect(uri, match_data)
148
+ end
149
+ end
150
+
151
+ response = @app.call(env)
152
+
153
+ if @errors && response[0] >= 400 && uri = @errors[response[0]]
154
+ error_request = env.merge("PATH_INFO" => uri, "REQUEST_METHOD" => "GET")
155
+ error_response = @app.call(error_request)
156
+
157
+ if error_response[0] >= 400
158
+ raise FailedRequestError.new(env['PATH_INFO'], response[0], uri, error_response[0])
159
+ else
160
+ # Feed the error code back with the error document
161
+ error_response[0] = response[0]
162
+ return error_response
163
+ end
164
+ else
165
+ return response
166
+ end
167
+ end
168
+ end
169
+
170
+ end
171
+ end