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