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,116 @@
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
+ require 'set'
19
+
20
+ module Utopia
21
+ module Middleware
22
+
23
+ class Requester
24
+ TTL_KEY = "utopia.requestor.ttl"
25
+ REQUESTOR_KEY = "utopia.requestor"
26
+ PATH_VARIABLES = Set.new
27
+ MAXIMUM_DEPTH = 5
28
+
29
+ class Response
30
+ def initialize(status, headers, body)
31
+ @status = status
32
+ @headers = headers
33
+ @body = body
34
+ end
35
+
36
+ attr :status
37
+ attr :headers
38
+
39
+ def body
40
+ unless @body_string
41
+ buffer = StringIO.new
42
+
43
+ @body.each do |string|
44
+ buffer.write(string)
45
+ end
46
+
47
+ @body_string = buffer.string
48
+ end
49
+
50
+ return @body_string
51
+ end
52
+
53
+ def [](key)
54
+ return @headers[key]
55
+ end
56
+
57
+ def okay?
58
+ @status == 200
59
+ end
60
+ end
61
+
62
+ class NoRequesterError < ArgumentError
63
+ end
64
+
65
+ def self.[](env)
66
+ requestor = env[REQUESTOR_KEY]
67
+ if requestor
68
+ return requestor
69
+ else
70
+ raise NoRequesterError
71
+ end
72
+ end
73
+
74
+ def initialize(app, env = {})
75
+ @app = app
76
+ @env = env
77
+ @env[TTL_KEY] = 0
78
+ end
79
+
80
+ attr :env, true
81
+
82
+ def call(env)
83
+ requestor = dup
84
+ env[REQUESTOR_KEY] = requestor
85
+ requestor.env = env.merge(@env)
86
+ requestor.env.delete("rack.request")
87
+
88
+ @app.call(env)
89
+ end
90
+
91
+ class RecursiveRequestError < StandardError
92
+ end
93
+
94
+ def request(env)
95
+ env = @env.merge(env)
96
+ env[TTL_KEY] += 1
97
+
98
+ if env[TTL_KEY].to_i > MAXIMUM_DEPTH
99
+ raise RecursiveRequestError, env["PATH_INFO"]
100
+ end
101
+
102
+ return Response.new(*@app.call(env))
103
+ end
104
+
105
+ def [](path, method = "GET", env = {})
106
+ path_env = {
107
+ "REQUEST_METHOD" => method,
108
+ "PATH_INFO" => path.to_s
109
+ }
110
+ request(env.merge(path_env))
111
+ end
112
+ end
113
+
114
+ end
115
+ end
116
+
@@ -0,0 +1,186 @@
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
+ require 'time'
20
+
21
+ require 'mime/types'
22
+
23
+ module Utopia
24
+ module Middleware
25
+
26
+ class Static
27
+ DEFAULT_TYPES = [
28
+ "html", "css", "js", "txt", "rtf", "xml",
29
+ "pdf", "zip", "tar", "tgz", "tar.gz", "tar.bz2", "dmg",
30
+ "mp3", "mp4", "wav", "aiff", ["aac", "audio/x-aac"], "mov", "avi", "wmv",
31
+ /^image/
32
+ ]
33
+
34
+ private
35
+
36
+ class FileReader
37
+ def initialize(path)
38
+ @path = path
39
+ end
40
+
41
+ attr :path
42
+
43
+ def to_path
44
+ @path
45
+ end
46
+
47
+ def mtime_date
48
+ File.mtime(@path).httpdate
49
+ end
50
+
51
+ def size
52
+ File.size(@path)
53
+ end
54
+
55
+ def each
56
+ File.open(@path, "rb") do |fp|
57
+ while part = fp.read(8192)
58
+ yield part
59
+ end
60
+ end
61
+ end
62
+ end
63
+
64
+ def load_mime_types(types)
65
+ result = {}
66
+
67
+ extract_extensions = lambda do |mime_type|
68
+ mime_type.extensions.each{|ext| result["." + ext] = mime_type.content_type}
69
+ end
70
+
71
+ types.each do |type|
72
+ current_count = result.size
73
+
74
+ begin
75
+ case type
76
+ when :defaults
77
+ result = load_mime_types(DEFAULT_TYPES).merge(result)
78
+ when Array
79
+ result["." + type[0]] = type[1]
80
+ when String
81
+ mt = MIME::Types.of(type).select{|mt| !mt.obsolete?}.each do |mt|
82
+ extract_extensions.call(mt)
83
+ end
84
+ when Regexp
85
+ MIME::Types[type].select{|mt| !mt.obsolete?}.each do |mt|
86
+ extract_extensions.call(mt)
87
+ end
88
+ when MIME::Type
89
+ extract_extensions.call(type)
90
+ end
91
+ rescue
92
+ LOG.error "#{self.class.name}: Error while processing #{type.inspect}!"
93
+ raise $!
94
+ end
95
+
96
+ if result.size == current_count
97
+ LOG.warn "#{self.class.name}: Could not find any mime type for file extension #{type.inspect}"
98
+ end
99
+ end
100
+
101
+ return result
102
+ end
103
+
104
+ public
105
+ def initialize(app, options = {})
106
+ @app = app
107
+ @root = options[:root] || Utopia::Middleware::default_root
108
+
109
+ if options[:types]
110
+ @extensions = load_mime_types(options[:types])
111
+ else
112
+ @extensions = load_mime_types(DEFAULT_TYPES)
113
+ end
114
+
115
+ LOG.info "#{self.class.name}: Running in #{@root} with #{extensions.size} filetypes"
116
+ end
117
+
118
+ def fetch_file(path)
119
+ file_path = File.join(@root, path.components)
120
+ if File.exist?(file_path)
121
+ return FileReader.new(file_path)
122
+ else
123
+ return nil
124
+ end
125
+ end
126
+
127
+ def lookup_relative_file(path)
128
+ file = nil
129
+ name = path.basename
130
+
131
+ if split = path.split("@rel@")
132
+ path = split[0]
133
+ name = split[1].components
134
+
135
+ # Fix a problem if the browser request has multiple @rel@
136
+ # This normally indicates a browser bug.. :(
137
+ name.delete("@rel@")
138
+ else
139
+ path = path.dirname
140
+
141
+ # Relative lookups are not done unless explicitly required by @rel@
142
+ # ... but they do work. This is a performance optimization.
143
+ return nil
144
+ end
145
+
146
+ # LOG.debug("Searching for #{name.inspect} starting in #{path.components}")
147
+
148
+ path.ascend do |parent_path|
149
+ file_path = File.join(@root, parent_path.components, name)
150
+ # LOG.debug("File path: #{file_path}")
151
+ if File.exist?(file_path)
152
+ return (parent_path + name).to_s
153
+ end
154
+ end
155
+
156
+ return nil
157
+ end
158
+
159
+ attr :extensions
160
+
161
+ def call(env)
162
+ request = Rack::Request.new(env)
163
+ ext = File.extname(request.path_info)
164
+ if @extensions.key? ext
165
+ path = Path.create(request.path_info).simplify
166
+
167
+ if file = fetch_file(path)
168
+ response_headers = {
169
+ "Last-Modified" => file.mtime_date,
170
+ "Content-Type" => @extensions[ext],
171
+ "Content-Length" => file.size.to_s
172
+ }
173
+
174
+ return [200, response_headers, file]
175
+ elsif redirect = lookup_relative_file(path)
176
+ return [307, {"Location" => redirect}, []]
177
+ end
178
+ end
179
+
180
+ # else if no file was found:
181
+ return @app.call(env)
182
+ end
183
+ end
184
+
185
+ end
186
+ end
@@ -0,0 +1,193 @@
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 'rack/utils'
17
+
18
+ module Utopia
19
+
20
+ class Path
21
+ SEPARATOR = "/"
22
+
23
+ def initialize(components)
24
+ # To ensure we don't do anything stupid we freeze the components
25
+ @components = components.dup.freeze
26
+ end
27
+
28
+ # Shorthand constructor
29
+ def self.[](path)
30
+ create(path)
31
+ end
32
+
33
+ def self.create(path)
34
+ case path
35
+ when Path
36
+ return path
37
+ when Array
38
+ return Path.new(path)
39
+ when String
40
+ path = Rack::Utils.unescape(path)
41
+ # Ends with SEPARATOR
42
+ if path[-1,1] == SEPARATOR
43
+ return Path.new(path.split(SEPARATOR) << "")
44
+ else
45
+ return Path.new(path.split(SEPARATOR))
46
+ end
47
+ end
48
+ end
49
+
50
+ attr :components
51
+
52
+ def directory?
53
+ return @components.last == ""
54
+ end
55
+
56
+ def to_directory
57
+ if directory?
58
+ return self
59
+ else
60
+ return join([""])
61
+ end
62
+ end
63
+
64
+ def absolute?
65
+ return @components.first == ""
66
+ end
67
+
68
+ def to_absolute
69
+ if absolute?
70
+ return self
71
+ else
72
+ return Path.new([""] + @components)
73
+ end
74
+ end
75
+
76
+ def to_s
77
+ if @components == [""]
78
+ SEPARATOR
79
+ else
80
+ @components.join(SEPARATOR)
81
+ end
82
+ end
83
+
84
+ def join(other)
85
+ Path.new(@components + other).simplify
86
+ end
87
+
88
+ def +(other)
89
+ if other.kind_of? Array
90
+ return join(other)
91
+ elsif other.kind_of? Path
92
+ if other.absolute?
93
+ return other
94
+ else
95
+ return join(other.components)
96
+ end
97
+ else
98
+ return join([other.to_s])
99
+ end
100
+ end
101
+
102
+ def simplify
103
+ result = absolute? ? [""] : []
104
+
105
+ components.each do |bit|
106
+ if bit == ".."
107
+ result.pop
108
+ elsif bit != "." && bit != ""
109
+ result << bit
110
+ end
111
+ end
112
+
113
+ result << "" if directory?
114
+ return Path.new(result)
115
+ end
116
+
117
+ def basename(ext = nil)
118
+ if ext
119
+ File.basename(components.last, ext)
120
+ else
121
+ components.last
122
+ end
123
+ end
124
+
125
+ def dirname(count = 1)
126
+ path = Path.new(components[0...-count])
127
+
128
+ return absolute? ? path.to_absolute : path
129
+ end
130
+
131
+ def to_local_path
132
+ components.join(File::SEPARATOR)
133
+ end
134
+
135
+ def ascend(&block)
136
+ paths = []
137
+
138
+ next_parent = self
139
+
140
+ begin
141
+ parent = next_parent
142
+
143
+ yield parent if block_given?
144
+ paths << parent
145
+
146
+ next_parent = parent.dirname
147
+ end until next_parent.eql?(parent)
148
+
149
+ return paths
150
+ end
151
+
152
+ def split(at)
153
+ if at.kind_of? String
154
+ at = @components.index(at)
155
+ end
156
+
157
+ if at
158
+ return [Path.new(@components[0...at]), Path.new(@components[at+1..-1])]
159
+ else
160
+ return nil
161
+ end
162
+ end
163
+
164
+ def dup
165
+ return Path.new(components.dup)
166
+ end
167
+
168
+ def <=> other
169
+ @components <=> other.components
170
+ end
171
+
172
+ def eql? other
173
+ if self.class == other.class
174
+ return @components.eql?(other.components)
175
+ else
176
+ return false
177
+ end
178
+ end
179
+
180
+ def == other
181
+ other.components.each_with_index do |part, index|
182
+ return false if @components[index] != part
183
+ end
184
+
185
+ return true
186
+ end
187
+
188
+ def hash
189
+ @components.hash
190
+ end
191
+ end
192
+
193
+ end