zorglub 0.1.0 → 0.1.1
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.
- checksums.yaml +4 -4
- data/Gemfile +1 -1
- data/Gemfile.lock +35 -36
- data/lib/zorglub/app.rb +107 -106
- data/lib/zorglub/engines/file.rb +8 -12
- data/lib/zorglub/engines/haml.rb +12 -17
- data/lib/zorglub/engines/sass.rb +11 -16
- data/lib/zorglub/node.rb +273 -273
- data/lib/zorglub/rack_session.rb +4 -6
- data/lib/zorglub/session.rb +142 -146
- data/lib/zorglub.rb +1 -4
- data/spec/app_spec.rb +26 -32
- data/spec/node_spec.rb +298 -304
- data/spec/spec_helper.rb +187 -170
- metadata +6 -7
data/lib/zorglub/node.rb
CHANGED
@@ -1,334 +1,334 @@
|
|
1
|
-
# -*- coding: UTF-8 -*-
|
2
|
-
|
3
1
|
require 'fileutils'
|
4
2
|
|
5
3
|
module Zorglub
|
4
|
+
class Node
|
5
|
+
UNDEFINED = -1
|
6
6
|
|
7
|
-
class
|
8
|
-
|
9
|
-
UNDEFINED=-1
|
10
|
-
|
11
|
-
# class level engine, layout, static, layout_base_path, view_base_path configuration
|
7
|
+
# class level engine, layout, static, layout_base_path, view_base_path configuration
|
12
8
|
|
13
|
-
|
9
|
+
class << self
|
10
|
+
attr_reader :static, :cache_lifetime
|
14
11
|
|
15
|
-
|
12
|
+
def engine!(engine)
|
13
|
+
@engine = engine
|
14
|
+
end
|
16
15
|
|
17
|
-
|
18
|
-
|
19
|
-
|
16
|
+
def engine
|
17
|
+
@engine = @app.opt(:engine) if @engine == UNDEFINED && @app
|
18
|
+
@engine
|
19
|
+
end
|
20
20
|
|
21
|
-
|
22
|
-
|
23
|
-
|
24
|
-
end
|
21
|
+
def no_layout!
|
22
|
+
layout! nil
|
23
|
+
end
|
25
24
|
|
26
|
-
|
27
|
-
|
28
|
-
|
25
|
+
def layout!(layout)
|
26
|
+
@layout = layout
|
27
|
+
end
|
29
28
|
|
30
|
-
|
31
|
-
|
32
|
-
|
29
|
+
def layout
|
30
|
+
@layout = @app.opt(:layout) if @layout == UNDEFINED && @app
|
31
|
+
@layout
|
32
|
+
end
|
33
33
|
|
34
|
-
|
35
|
-
|
36
|
-
|
37
|
-
|
34
|
+
def static!(val, lifetime = 0)
|
35
|
+
@static = [true, false].include?(val) ? val : false
|
36
|
+
@cache_lifetime = lifetime
|
37
|
+
end
|
38
38
|
|
39
|
-
|
40
|
-
|
41
|
-
|
42
|
-
end
|
39
|
+
def layout_base_path!(path)
|
40
|
+
@layout_base_path = path
|
41
|
+
end
|
43
42
|
|
44
|
-
|
45
|
-
|
46
|
-
|
43
|
+
def layout_base_path
|
44
|
+
@layout_base_path ||= @app.layout_base_path
|
45
|
+
end
|
47
46
|
|
48
|
-
|
49
|
-
|
50
|
-
|
47
|
+
def view_base_path!(path)
|
48
|
+
@view_base_path = path
|
49
|
+
end
|
51
50
|
|
52
|
-
|
53
|
-
|
54
|
-
|
55
|
-
|
56
|
-
def view_base_path
|
57
|
-
@view_base_path ||= @app.view_base_path
|
58
|
-
end
|
59
|
-
end
|
60
|
-
|
61
|
-
# instance level engine, layout, view, static configuration
|
51
|
+
def view_base_path
|
52
|
+
@view_base_path ||= @app.view_base_path
|
53
|
+
end
|
54
|
+
end
|
62
55
|
|
63
|
-
|
64
|
-
@engine = engine
|
65
|
-
end
|
56
|
+
# instance level engine, layout, view, static configuration
|
66
57
|
|
67
|
-
|
68
|
-
|
69
|
-
|
58
|
+
def engine!(engine)
|
59
|
+
@engine = engine
|
60
|
+
end
|
70
61
|
|
71
|
-
|
72
|
-
|
73
|
-
|
62
|
+
def no_layout!
|
63
|
+
layout! nil
|
64
|
+
end
|
74
65
|
|
75
|
-
|
76
|
-
|
77
|
-
|
78
|
-
end
|
66
|
+
def layout!(layout)
|
67
|
+
@layout = layout
|
68
|
+
end
|
79
69
|
|
80
|
-
|
81
|
-
|
82
|
-
end
|
70
|
+
def layout
|
71
|
+
return nil if @layout.nil?
|
83
72
|
|
84
|
-
|
85
|
-
|
86
|
-
end
|
73
|
+
File.join(self.class.layout_base_path, @layout) + ext
|
74
|
+
end
|
87
75
|
|
88
|
-
|
89
|
-
|
90
|
-
|
91
|
-
end
|
76
|
+
def no_view!
|
77
|
+
view! nil
|
78
|
+
end
|
92
79
|
|
93
|
-
|
94
|
-
|
95
|
-
|
96
|
-
end
|
80
|
+
def view!(view)
|
81
|
+
@view = view
|
82
|
+
end
|
97
83
|
|
98
|
-
|
99
|
-
|
100
|
-
File.join(app.static_base_path, @view) + ext
|
101
|
-
end
|
84
|
+
def view
|
85
|
+
return nil if @view.nil?
|
102
86
|
|
103
|
-
|
104
|
-
|
105
|
-
end
|
87
|
+
File.join(self.class.view_base_path, @view) + ext
|
88
|
+
end
|
106
89
|
|
107
|
-
|
108
|
-
|
109
|
-
|
90
|
+
def static!(val, lifetime = 0)
|
91
|
+
@static = [true, false].include?(val) ? val : false
|
92
|
+
@cache_lifetime = lifetime
|
93
|
+
end
|
110
94
|
|
111
|
-
|
112
|
-
|
113
|
-
end
|
95
|
+
def static
|
96
|
+
return nil if !@static || @view.nil?
|
114
97
|
|
115
|
-
|
98
|
+
File.join(app.static_base_path, @view) + ext
|
99
|
+
end
|
116
100
|
|
117
|
-
|
101
|
+
def ext!(ext)
|
102
|
+
@ext = if ext.nil? || ext.empty?
|
103
|
+
nil
|
104
|
+
elsif ext[0] == '.'
|
105
|
+
ext.length == 1 ? nil : ext
|
106
|
+
else
|
107
|
+
".#{ext}"
|
108
|
+
end
|
109
|
+
end
|
118
110
|
|
119
|
-
|
120
|
-
|
121
|
-
|
122
|
-
@app.map location, self
|
123
|
-
end
|
111
|
+
def ext
|
112
|
+
@ext || ''
|
113
|
+
end
|
124
114
|
|
125
|
-
|
126
|
-
|
127
|
-
|
128
|
-
end
|
115
|
+
def mime!(mime)
|
116
|
+
@mime = mime
|
117
|
+
end
|
129
118
|
|
130
|
-
|
119
|
+
# class level basic node functions
|
131
120
|
|
132
|
-
|
121
|
+
class << self
|
122
|
+
attr_accessor :app
|
133
123
|
|
134
|
-
|
135
|
-
|
136
|
-
|
124
|
+
def map(app, location)
|
125
|
+
@app = app
|
126
|
+
@app.map location, self
|
127
|
+
end
|
137
128
|
|
138
|
-
|
139
|
-
|
140
|
-
|
129
|
+
def r *args
|
130
|
+
@r ||= @app.to self
|
131
|
+
args.empty? ? @r : File.join(@r, args.map(&:to_s))
|
132
|
+
end
|
133
|
+
end
|
141
134
|
|
142
|
-
|
143
|
-
File.join map, (args.empty? ? meth : args.map { |x| x.to_s } )
|
144
|
-
end
|
135
|
+
# instance level basic node functions
|
145
136
|
|
146
|
-
|
147
|
-
|
148
|
-
|
137
|
+
def app
|
138
|
+
self.class.app
|
139
|
+
end
|
149
140
|
|
150
|
-
|
151
|
-
|
152
|
-
|
153
|
-
header = response.header.merge('Location' => target.to_s)
|
154
|
-
throw :stop_realize, Rack::Response.new(body, status, header, &block)
|
155
|
-
end
|
141
|
+
def map
|
142
|
+
self.class.r
|
143
|
+
end
|
156
144
|
|
157
|
-
|
158
|
-
|
159
|
-
|
145
|
+
def r *args
|
146
|
+
File.join map, (args.empty? ? meth : args.map(&:to_s))
|
147
|
+
end
|
160
148
|
|
161
|
-
|
162
|
-
|
163
|
-
|
164
|
-
# so that can be extanded without modifying class level values
|
165
|
-
# typical usage are css or js inclusions
|
149
|
+
def html
|
150
|
+
%i[map r args engine layout view].inject('') { |s, sym| s + "<p>#{sym} => #{send sym}</p>" }
|
151
|
+
end
|
166
152
|
|
167
|
-
|
153
|
+
def redirect(target, options = {}, &block)
|
154
|
+
status = options[:status] || 302
|
155
|
+
body = options[:body] || redirect_body(target)
|
156
|
+
header = response.headers.merge('Location' => target.to_s)
|
157
|
+
throw :stop_realize, Rack::Response.new(body, status, header, &block)
|
158
|
+
end
|
168
159
|
|
169
|
-
|
160
|
+
def redirect_body(target)
|
161
|
+
"You are being redirected, please follow this link to: <a href='#{target}'>#{target}</a>!"
|
162
|
+
end
|
170
163
|
|
171
|
-
|
164
|
+
# class level inherited values are key=>array, copied at inheritance
|
165
|
+
# so they can be extanded at class level
|
166
|
+
# values are copied from class into instance at object creation
|
167
|
+
# so that can be extanded without modifying class level values
|
168
|
+
# typical usage are css or js inclusions
|
172
169
|
|
173
|
-
|
174
|
-
vals = @cli_vals[sym] ||= []
|
175
|
-
unless args.empty?
|
176
|
-
vals.concat args
|
177
|
-
vals.uniq!
|
178
|
-
end
|
179
|
-
vals
|
180
|
-
end
|
170
|
+
@cli_vals = {}
|
181
171
|
|
182
|
-
|
172
|
+
class << self
|
173
|
+
attr_reader :cli_vals
|
183
174
|
|
184
|
-
|
185
|
-
|
186
|
-
|
187
|
-
|
188
|
-
|
189
|
-
end
|
190
|
-
vals
|
175
|
+
def cli_val(sym, *args)
|
176
|
+
vals = @cli_vals[sym] ||= []
|
177
|
+
unless args.empty?
|
178
|
+
vals.concat args
|
179
|
+
vals.uniq!
|
191
180
|
end
|
181
|
+
vals
|
182
|
+
end
|
183
|
+
end
|
192
184
|
|
193
|
-
|
194
|
-
|
195
|
-
|
196
|
-
|
197
|
-
|
198
|
-
|
199
|
-
|
200
|
-
|
201
|
-
end
|
202
|
-
|
203
|
-
def before_all meth=nil, &blk
|
204
|
-
@cli_vals[:before_all]<< ( meth.nil? ? blk : meth )
|
205
|
-
@cli_vals[:before_all].uniq!
|
206
|
-
end
|
207
|
-
|
208
|
-
def call_after_hooks obj
|
209
|
-
@cli_vals[:after_all].each do |blk| blk.call obj end
|
210
|
-
end
|
185
|
+
def cli_val(sym, *args)
|
186
|
+
vals = @cli_vals[sym] ||= []
|
187
|
+
unless args.empty?
|
188
|
+
vals.concat args
|
189
|
+
vals.uniq!
|
190
|
+
end
|
191
|
+
vals
|
192
|
+
end
|
211
193
|
|
212
|
-
|
213
|
-
@cli_vals[:after_all]<< ( meth.nil? ? blk : meth )
|
214
|
-
@cli_vals[:after_all].uniq!
|
215
|
-
end
|
194
|
+
# before_all and after_all hooks
|
216
195
|
|
217
|
-
|
196
|
+
@cli_vals[:before_all] = []
|
197
|
+
@cli_vals[:after_all] = []
|
198
|
+
class << self
|
199
|
+
def call_before_hooks(obj)
|
200
|
+
@cli_vals[:before_all].each { |blk| blk.call obj }
|
201
|
+
end
|
218
202
|
|
219
|
-
|
220
|
-
|
221
|
-
|
222
|
-
|
223
|
-
def inherited sub
|
224
|
-
sub.engine! ( engine || (self==Zorglub::Node ? UNDEFINED : nil ) )
|
225
|
-
sub.layout! ( layout || (self==Zorglub::Node ? UNDEFINED : nil ) )
|
226
|
-
sub.instance_variable_set :@cli_vals, {}
|
227
|
-
@cli_vals.each do |s,v| sub.cli_val s, *v end
|
228
|
-
end
|
229
|
-
|
230
|
-
def call env
|
231
|
-
meth, *args = env['PATH_INFO'].sub(/^\/+/,'').split(/\//)
|
232
|
-
meth ||= 'index'
|
233
|
-
$stderr << "=> #{meth}(#{args.join ','})\n" if app.opt :debug
|
234
|
-
node = self.new env, meth, args
|
235
|
-
return error_404 node, meth if not node.respond_to? meth
|
236
|
-
node.realize!
|
237
|
-
end
|
238
|
-
|
239
|
-
def partial env, meth, *args
|
240
|
-
node = self.new env, meth.to_s, args, true
|
241
|
-
return error_404 node, meth if not node.respond_to? meth
|
242
|
-
node.feed! env[:no_hooks]
|
243
|
-
node.content
|
244
|
-
end
|
245
|
-
|
246
|
-
def error_404 node, meth
|
247
|
-
$stderr << " !! #{node.class.name}::#{meth} not found\n" if app.opt :debug
|
248
|
-
resp = node.response
|
249
|
-
resp.status = 404
|
250
|
-
resp['Content-Type'] = 'text/plain'
|
251
|
-
resp.write "%s mapped at %p can't respond to : %p" % [ node.class.name, node.map, node.meth ]
|
252
|
-
resp
|
253
|
-
end
|
203
|
+
def before_all(meth = nil, &blk)
|
204
|
+
@cli_vals[:before_all] << (meth.nil? ? blk : meth)
|
205
|
+
@cli_vals[:before_all].uniq!
|
206
|
+
end
|
254
207
|
|
255
|
-
|
208
|
+
def call_after_hooks(obj)
|
209
|
+
@cli_vals[:after_all].each { |blk| blk.call obj }
|
210
|
+
end
|
256
211
|
|
257
|
-
|
258
|
-
|
259
|
-
|
260
|
-
|
261
|
-
|
262
|
-
@partial = partial
|
263
|
-
@request = Rack::Request.new env
|
264
|
-
@response = Rack::Response.new
|
265
|
-
@cli_vals ={}
|
266
|
-
@debug = app.opt :debug
|
267
|
-
@engine = self.class.engine
|
268
|
-
@layout = ( partial ? nil : self.class.layout )
|
269
|
-
@view = r(meth)
|
270
|
-
@static = self.class.static
|
271
|
-
@cache_lifetime = self.class.cache_lifetime
|
272
|
-
self.class.cli_vals.each do |s,v| cli_val s, *v end
|
273
|
-
end
|
212
|
+
def after_all(meth = nil, &blk)
|
213
|
+
@cli_vals[:after_all] << (meth.nil? ? blk : meth)
|
214
|
+
@cli_vals[:after_all].uniq!
|
215
|
+
end
|
216
|
+
end
|
274
217
|
|
275
|
-
|
276
|
-
|
277
|
-
|
278
|
-
|
279
|
-
|
280
|
-
|
281
|
-
|
282
|
-
|
283
|
-
|
218
|
+
# rack entry point, page computation methods
|
219
|
+
|
220
|
+
class << self
|
221
|
+
def inherited(sub)
|
222
|
+
super
|
223
|
+
sub.engine!(engine || (self == Zorglub::Node ? UNDEFINED : nil))
|
224
|
+
sub.layout!(layout || (self == Zorglub::Node ? UNDEFINED : nil))
|
225
|
+
sub.instance_variable_set :@cli_vals, {}
|
226
|
+
@cli_vals.each { |s, v| sub.cli_val s, *v }
|
227
|
+
end
|
228
|
+
|
229
|
+
def call(env)
|
230
|
+
meth, *args = env['PATH_INFO'].sub(%r{^/+}, '').split(%r{/})
|
231
|
+
meth ||= 'index'
|
232
|
+
$stderr << "=> #{meth}(#{args.join ','})\n" if app.opt :debug
|
233
|
+
node = new(env, meth, args)
|
234
|
+
return error404 node, meth unless node.respond_to? meth
|
235
|
+
|
236
|
+
node.realize!
|
237
|
+
end
|
238
|
+
|
239
|
+
def partial(env, meth, *args)
|
240
|
+
node = new env, meth.to_s, args, true
|
241
|
+
return error404 node, meth unless node.respond_to? meth
|
242
|
+
|
243
|
+
node.feed! env[:no_hooks]
|
244
|
+
node.content
|
245
|
+
end
|
246
|
+
|
247
|
+
def error404(node, meth)
|
248
|
+
$stderr << " !! #{node.class.name}::#{meth} not found\n" if app.opt :debug
|
249
|
+
resp = node.response
|
250
|
+
resp.status = 404
|
251
|
+
resp['Content-Type'] = 'text/plain'
|
252
|
+
resp.write "%<node.class.name>s mapped at %<node.map>p can't respond to : %<node.meth>p"
|
253
|
+
resp
|
254
|
+
end
|
255
|
+
end
|
284
256
|
|
285
|
-
|
286
|
-
|
287
|
-
|
288
|
-
|
289
|
-
|
290
|
-
|
291
|
-
|
292
|
-
|
293
|
-
|
294
|
-
|
295
|
-
|
296
|
-
|
297
|
-
|
298
|
-
|
299
|
-
|
300
|
-
|
257
|
+
attr_reader :request, :response, :content, :mime, :state, :engine, :meth, :args
|
258
|
+
|
259
|
+
def initialize(env, meth, args, partial = false)
|
260
|
+
@meth = meth
|
261
|
+
@args = args
|
262
|
+
@partial = partial
|
263
|
+
@request = Rack::Request.new env
|
264
|
+
@response = Rack::Response.new
|
265
|
+
@cli_vals = {}
|
266
|
+
@debug = app.opt :debug
|
267
|
+
@engine = self.class.engine
|
268
|
+
@layout = (partial ? nil : self.class.layout)
|
269
|
+
@view = r(meth)
|
270
|
+
@static = self.class.static
|
271
|
+
@cache_lifetime = self.class.cache_lifetime
|
272
|
+
self.class.cli_vals.each { |s, v| cli_val s, *v }
|
273
|
+
end
|
301
274
|
|
302
|
-
|
303
|
-
|
304
|
-
|
305
|
-
|
306
|
-
|
307
|
-
|
308
|
-
|
309
|
-
|
310
|
-
|
311
|
-
File.open(path, 'w') {|f| f.write("@mime:"+@mime+"\n"); f.write(@content); }
|
312
|
-
$stderr << " * cache file created : #{path}\n" if @debug
|
313
|
-
end
|
314
|
-
end
|
275
|
+
def realize!
|
276
|
+
catch(:stop_realize) do
|
277
|
+
feed!
|
278
|
+
response.write @content
|
279
|
+
response.headers['Content-Type'] ||= @mime || 'text/html'
|
280
|
+
response.finish
|
281
|
+
response
|
282
|
+
end
|
283
|
+
end
|
315
284
|
|
316
|
-
|
317
|
-
|
318
|
-
|
319
|
-
|
320
|
-
|
321
|
-
|
322
|
-
|
323
|
-
|
324
|
-
|
325
|
-
|
326
|
-
|
327
|
-
|
328
|
-
|
329
|
-
|
330
|
-
|
285
|
+
def feed!(no_hooks = false)
|
286
|
+
@state = :pre_cb
|
287
|
+
self.class.call_before_hooks self unless no_hooks
|
288
|
+
@state = :meth
|
289
|
+
@content = send @meth, *@args
|
290
|
+
static_path = static
|
291
|
+
if static_path.nil?
|
292
|
+
compile_page!
|
293
|
+
else
|
294
|
+
static_page! static_path
|
295
|
+
end
|
296
|
+
@state = :post_cb
|
297
|
+
self.class.call_after_hooks self unless no_hooks
|
298
|
+
@state = :finished
|
299
|
+
[@content, @mime]
|
300
|
+
end
|
331
301
|
|
302
|
+
def static_page!(path)
|
303
|
+
if File.exist?(path) && (@cache_lifetime.nil? || @cache_lifetime.zero? ||
|
304
|
+
(Time.now - File.stat(path).mtime) < @cache_lifetime)
|
305
|
+
$stderr << " * use cache file : #{path}\n" if @debug
|
306
|
+
content = File.read(path)
|
307
|
+
@content = content.sub(/^@mime:(.*)\n/, '')
|
308
|
+
@mime = ::Regexp.last_match(1)
|
309
|
+
else
|
310
|
+
compile_page!
|
311
|
+
FileUtils.mkdir_p File.dirname(path)
|
312
|
+
File.open(path, 'w') { |f| f.write("@mime:#{@mime}\n#{@content}") }
|
313
|
+
$stderr << " * cache file created : #{path}\n" if @debug
|
314
|
+
end
|
332
315
|
end
|
333
316
|
|
317
|
+
def compile_page!
|
318
|
+
e, @ext = app.engine_proc_ext @engine, @ext
|
319
|
+
v = view
|
320
|
+
l = layout
|
321
|
+
if @debug
|
322
|
+
$stderr << " * #{e ? 'use engine' : 'no engine '} : #{e ? e.to_s : ''}\n"
|
323
|
+
$stderr << " * #{l && File.exist?(l) ? 'use layout' : 'no layout '} : #{l || ''}\n"
|
324
|
+
$stderr << " * #{v && File.exist?(v) ? 'use view ' : 'no view '} : #{v || ''}\n"
|
325
|
+
end
|
326
|
+
@state = @partial ? :partial : :view
|
327
|
+
@content, mime = e.call(v, self) if e && v && File.exist?(v)
|
328
|
+
@mime ||= mime
|
329
|
+
@state = :layout
|
330
|
+
@content, mime = e.call(l, self) if e && l && File.exist?(l)
|
331
|
+
@mime = mime if @mime.nil? && !mime.nil?
|
332
|
+
end
|
333
|
+
end
|
334
334
|
end
|