utopia 0.9.17

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