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