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,363 @@
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 'set'
17
+
18
+ require 'utopia/xnode'
19
+ require 'utopia/link'
20
+
21
+ module Utopia
22
+
23
+ module Middleware
24
+ class Content
25
+ class UnbalancedTagError < StandardError
26
+ def initialize(tag)
27
+ @tag = tag
28
+
29
+ super("Unbalanced tag #{tag.name}")
30
+ end
31
+
32
+ attr :tag
33
+ end
34
+
35
+ # Nodes typically represent XNODE files on the disk.
36
+ # You can get a list of Links from a current directory. This comprises of all
37
+ # files ending in ".xnode".
38
+
39
+ class Transaction
40
+ class State
41
+ def initialize(tag, node)
42
+ @node = node
43
+
44
+ @buffer = StringIO.new
45
+ @overrides = {}
46
+
47
+ @tags = []
48
+ @attributes = tag.to_hash
49
+
50
+ @content = nil
51
+ end
52
+
53
+ attr :attributes
54
+ attr :overrides
55
+ attr :content
56
+ attr :node
57
+ attr :tags
58
+
59
+ def [](key)
60
+ @attributes[key.to_s]
61
+ end
62
+
63
+ def call(transaction)
64
+ @content = @buffer.string
65
+ @buffer = StringIO.new
66
+
67
+ if node.respond_to? :call
68
+ node.call(transaction, self)
69
+ else
70
+ transaction.parse_xml(@content)
71
+ end
72
+
73
+ return @buffer.string
74
+ end
75
+
76
+ def lookup(tag)
77
+ if override = @overrides[tag.name]
78
+ if override.respond_to? :call
79
+ return override.call(tag)
80
+ elsif String === override
81
+ return Tag.new(override, tag.attributes)
82
+ else
83
+ return override
84
+ end
85
+ else
86
+ return tag
87
+ end
88
+ end
89
+
90
+ def cdata(text)
91
+ @buffer.write(text)
92
+ end
93
+
94
+ def markup(text)
95
+ cdata(text)
96
+ end
97
+
98
+ def tag_complete(tag)
99
+ tag.write_full_html(@buffer)
100
+ end
101
+
102
+ def tag_begin(tag)
103
+ @tags << tag
104
+ tag.write_open_html(@buffer)
105
+ end
106
+
107
+ def tag_end(tag)
108
+ raise UnbalancedTagError(tag) unless @tags.pop.name == tag.name
109
+
110
+ tag.write_close_html(@buffer)
111
+ end
112
+ end
113
+
114
+ def initialize(request, response)
115
+ @begin_tags = []
116
+ @end_tags = []
117
+
118
+ @request = request
119
+ @response = response
120
+ end
121
+
122
+ def binding
123
+ super
124
+ end
125
+
126
+ def parse_xml(xml_data)
127
+ XNode::Processor.new(xml_data, self).parse
128
+ end
129
+
130
+ attr :request
131
+ attr :response
132
+
133
+ # Begin tags represents a list from outer to inner most tag.
134
+ # At any point in parsing xml, begin_tags is a list of the inner most tag,
135
+ # then the next outer tag, etc. This list is used for doing dependent lookups.
136
+ attr :begin_tags
137
+
138
+ # End tags represents a list of execution order. This is the order that end tags
139
+ # have appeared when evaluating nodes.
140
+ attr :end_tags
141
+
142
+ def attributes
143
+ return current.attributes
144
+ end
145
+
146
+ def current
147
+ @begin_tags[-1]
148
+ end
149
+
150
+ def content
151
+ @end_tags[-1].content
152
+ end
153
+
154
+ def parent
155
+ # @begin_tags[-2]
156
+ end_tags[-2]
157
+ end
158
+
159
+ def first
160
+ @begin_tags[0]
161
+ end
162
+
163
+ def tag(name, attributes, &block)
164
+ tag = Tag.new(name, attributes)
165
+
166
+ node = tag_begin(tag)
167
+
168
+ yield node if block_given?
169
+
170
+ tag_end(tag)
171
+ end
172
+
173
+ def tag_complete(tag, node = nil)
174
+ if tag.name == "content"
175
+ current.markup(content)
176
+ else
177
+ node ||= lookup(tag)
178
+
179
+ if node
180
+ tag_begin(tag, node)
181
+ tag_end(tag)
182
+ else
183
+ current.tag_complete(tag)
184
+ end
185
+ end
186
+ end
187
+
188
+ def tag_begin(tag, node = nil)
189
+ # LOG.debug("tag_begin: #{tag}")
190
+ node ||= lookup(tag)
191
+
192
+ if node
193
+ state = State.new(tag, node)
194
+ @begin_tags << state
195
+
196
+ if node.respond_to? :tag_begin
197
+ node.tag_begin(self, state)
198
+ end
199
+
200
+ return node
201
+ end
202
+
203
+ current.tag_begin(tag)
204
+
205
+ return nil
206
+ end
207
+
208
+ def cdata(text)
209
+ # LOG.debug("cdata: #{text}")
210
+ current.cdata(text)
211
+ end
212
+
213
+ def tag_end(tag = nil)
214
+ top = current
215
+
216
+ if top.tags.empty?
217
+ # LOG.debug("tag_end: #{top.inspect}")
218
+
219
+ if top.node.respond_to? :tag_end
220
+ top.node.tag_end(self, top)
221
+ end
222
+
223
+ @end_tags << top
224
+ buffer = top.call(self)
225
+
226
+ @begin_tags.pop
227
+ @end_tags.pop
228
+
229
+ if current
230
+ current.markup(buffer)
231
+ end
232
+
233
+ return buffer
234
+ else
235
+ # LOG.debug("tag_end: #{tag}")
236
+ current.tag_end(tag)
237
+ end
238
+
239
+ return nil
240
+ end
241
+
242
+ def render_node(node, attributes = {})
243
+ state = State.new(attributes, node)
244
+ @begin_tags << state
245
+
246
+ return tag_end
247
+ end
248
+
249
+ def lookup(tag)
250
+ result = tag
251
+ node = nil
252
+
253
+ @begin_tags.reverse_each do |state|
254
+ result = state.lookup(result)
255
+
256
+ node ||= state.node if state.node.respond_to? :lookup
257
+
258
+ return result if Node === result
259
+ end
260
+
261
+ @end_tags.reverse_each do |state|
262
+ return state.node.lookup(result) if state.node.respond_to? :lookup
263
+ end
264
+
265
+ return nil
266
+ end
267
+
268
+ def method_missing(name, *args)
269
+ @begin_tags.reverse_each do |state|
270
+ if state.node.respond_to? name
271
+ return state.node.send(name, *args)
272
+ end
273
+ end
274
+
275
+ super
276
+ end
277
+ end
278
+
279
+ class Node
280
+ def initialize(controller, uri_path, request_path, file_path)
281
+ @controller = controller
282
+
283
+ @uri_path = uri_path
284
+ @request_path = request_path
285
+ @file_path = file_path
286
+ end
287
+
288
+ attr :request_path
289
+ attr :uri_path
290
+ attr :file_path
291
+
292
+ def link
293
+ return Link.new(:file, uri_path)
294
+ end
295
+
296
+ def lookup_node(path)
297
+ @controller.lookup_node(path)
298
+ end
299
+
300
+ def local_path(path, base = nil)
301
+ path = Path.create(path)
302
+
303
+ if path.absolute?
304
+ return File.join(@controller.root, path.components)
305
+ else
306
+ base ||= uri_path.dirname
307
+ return File.join(@controller.root, (base + path).components)
308
+ end
309
+ end
310
+
311
+ def lookup(tag)
312
+ return @controller.lookup_tag(tag.name, parent_path)
313
+ end
314
+
315
+ def parent_path
316
+ uri_path.dirname
317
+ end
318
+
319
+ def links(path, options = {}, &block)
320
+ path = uri_path.dirname + Path.create(path)
321
+ links = Links.index(@controller.root, path, options)
322
+
323
+ if block_given?
324
+ links.each &block
325
+ else
326
+ links
327
+ end
328
+ end
329
+
330
+ def related_links
331
+ name = @uri_path.basename.split(".").first
332
+ links = Links.index(@controller.root, uri_path.dirname, :name => name, :indices => true)
333
+ end
334
+
335
+ def siblings_path
336
+ name = @uri_path.basename.split(".").first
337
+
338
+ if name == "index"
339
+ @uri_path.dirname(2)
340
+ else
341
+ @uri_path.dirname
342
+ end
343
+ end
344
+
345
+ def sibling_links(options = {})
346
+ return Links.index(@controller.root, siblings_path, options)
347
+ end
348
+
349
+ def call(transaction, state)
350
+ xml_data = @controller.fetch_xml(@file_path).result(transaction.binding)
351
+
352
+ transaction.parse_xml(xml_data)
353
+ end
354
+
355
+ def process!(request, response)
356
+ transaction = Transaction.new(request, response)
357
+ response.body = [transaction.render_node(self)]
358
+ end
359
+ end
360
+
361
+ end
362
+ end
363
+ end
@@ -0,0 +1,198 @@
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
+ class Rack::Request
20
+ def controller(&block)
21
+ if block_given?
22
+ env["utopia.controller"].instance_eval(&block)
23
+ else
24
+ env["utopia.controller"]
25
+ end
26
+ end
27
+ end
28
+
29
+ module Utopia
30
+ module Middleware
31
+
32
+ class Controller
33
+ CONTROLLER_RB = "controller.rb"
34
+
35
+ class Variables
36
+ def [](key)
37
+ instance_variable_get("@#{key}")
38
+ end
39
+
40
+ def []=(key, value)
41
+ instance_variable_set("@#{key}", value)
42
+ end
43
+ end
44
+
45
+ class Base
46
+ def initialize(controller)
47
+ @controller = controller
48
+ @actions = {}
49
+
50
+ methods.each do |method_name|
51
+ next unless method_name.match(/on_(.*)$/)
52
+
53
+ action($1.split("_")) do |path, request|
54
+ # LOG.debug("Controller: #{method_name}")
55
+ self.send(method_name, path, request)
56
+ end
57
+ end
58
+ end
59
+
60
+ def action(path, options = {}, &block)
61
+ cur = @actions
62
+
63
+ path.reverse.each do |name|
64
+ cur = cur[name] ||= {}
65
+ end
66
+
67
+ cur[:action] = Proc.new(&block)
68
+ end
69
+
70
+ def lookup(path)
71
+ cur = @actions
72
+
73
+ path.components.reverse.each do |name|
74
+ cur = cur[name]
75
+
76
+ return nil if cur == nil
77
+
78
+ if action = cur[:action]
79
+ return action
80
+ end
81
+ end
82
+ end
83
+
84
+ # Given a request, call an associated action if one exists
85
+ def passthrough(path, request)
86
+ action = lookup(path)
87
+
88
+ if action
89
+ action.call(path, request)
90
+ else
91
+ return nil
92
+ end
93
+ end
94
+
95
+ def permission_denied
96
+ [403, {}, ["Permission Denied!"]]
97
+ end
98
+
99
+ def call(env)
100
+ @controller.app.call(env)
101
+ end
102
+
103
+ def redirect(target, status=302)
104
+ Rack::Response.new([], status, "Location" => target.to_s).finish
105
+ end
106
+
107
+ def permission_denied
108
+ [403, {}, ["Permission Denied!"]]
109
+ end
110
+
111
+ def process!(path, request)
112
+ end
113
+
114
+ def self.require_local(path)
115
+ require(File.join(const_get('BASE_PATH'), path))
116
+ end
117
+ end
118
+
119
+ def initialize(app, options = {})
120
+ @app = app
121
+ @root = options[:root] || Utopia::Middleware::default_root
122
+
123
+ LOG.info "#{self.class.name}: Running in #{@root}"
124
+
125
+ @controllers = {}
126
+ @cache_controllers = true
127
+
128
+ if options[:controller_file]
129
+ @controller_file = options[:controller_file]
130
+ else
131
+ @controller_file = "controller.rb"
132
+ end
133
+ end
134
+
135
+ attr :app
136
+
137
+ def lookup(path)
138
+ if @cache_controllers
139
+ return @controllers.fetch(path.to_s) do |key|
140
+ @controllers[key] = load_file(path)
141
+ end
142
+ else
143
+ return load_file(path)
144
+ end
145
+ end
146
+
147
+ def load_file(path)
148
+ if path.directory?
149
+ base_path = File.join(@root, path.components)
150
+ else
151
+ base_path = File.join(@root, path.dirname.components)
152
+ end
153
+
154
+ controller_path = File.join(base_path, CONTROLLER_RB)
155
+
156
+ if File.exist?(controller_path)
157
+ klass = Class.new(Base)
158
+ klass.const_set('BASE_PATH', base_path)
159
+
160
+ $LOAD_PATH.unshift(base_path)
161
+
162
+ klass.class_eval(File.read(controller_path), controller_path)
163
+
164
+ $LOAD_PATH.delete(base_path)
165
+
166
+ return klass.new(self)
167
+ else
168
+ return nil
169
+ end
170
+ end
171
+
172
+ def fetch_controllers(path)
173
+ controllers = []
174
+ path.ascend do |parent_path|
175
+ controllers << lookup(parent_path)
176
+ end
177
+
178
+ return controllers.compact.reverse
179
+ end
180
+
181
+ def call(env)
182
+ env["utopia.controller"] ||= Variables.new
183
+
184
+ request = Rack::Request.new(env)
185
+
186
+ path = Path.create(request.path_info)
187
+ fetch_controllers(path).each do |controller|
188
+ if result = controller.process!(path, request)
189
+ return result
190
+ end
191
+ end
192
+
193
+ return @app.call(env)
194
+ end
195
+ end
196
+
197
+ end
198
+ end