nitro 0.20.0 → 0.21.0
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.
- data/CHANGELOG +752 -543
- data/INSTALL +38 -38
- data/README +264 -225
- data/Rakefile +48 -49
- data/bin/nitro +3 -3
- data/bin/nitrogen +6 -6
- data/doc/AUTHORS +10 -10
- data/doc/CHANGELOG.1 +1939 -1939
- data/doc/CHANGELOG.2 +954 -954
- data/doc/LICENSE +3 -3
- data/doc/MIGRATION +28 -0
- data/doc/RELEASES +814 -643
- data/doc/config.txt +5 -5
- data/install.rb +7 -17
- data/lib/nitro.rb +38 -9
- data/lib/nitro/adapter/cgi.rb +311 -312
- data/lib/nitro/adapter/fastcgi.rb +18 -25
- data/lib/nitro/adapter/webrick.rb +128 -137
- data/lib/nitro/adapter/wee.rb +51 -0
- data/lib/nitro/caching.rb +20 -20
- data/lib/nitro/caching/actions.rb +43 -43
- data/lib/nitro/caching/fragments.rb +46 -46
- data/lib/nitro/caching/invalidation.rb +11 -11
- data/lib/nitro/caching/output.rb +65 -65
- data/lib/nitro/caching/stores.rb +67 -67
- data/lib/nitro/compiler.rb +262 -0
- data/lib/nitro/compiler/elements.rb +0 -0
- data/lib/nitro/compiler/errors.rb +65 -0
- data/lib/nitro/compiler/localization.rb +25 -0
- data/lib/nitro/compiler/markup.rb +19 -0
- data/lib/nitro/compiler/shaders.rb +206 -0
- data/lib/nitro/compiler/squeeze.rb +20 -0
- data/lib/nitro/compiler/xslt.rb +61 -0
- data/lib/nitro/context.rb +87 -88
- data/lib/nitro/controller.rb +151 -158
- data/lib/nitro/cookie.rb +34 -34
- data/lib/nitro/dispatcher.rb +195 -186
- data/lib/nitro/element.rb +132 -126
- data/lib/nitro/element/java_script.rb +6 -6
- data/lib/nitro/flash.rb +66 -66
- data/lib/nitro/mail.rb +192 -192
- data/lib/nitro/mixin/buffer.rb +66 -0
- data/lib/nitro/mixin/debug.rb +16 -16
- data/lib/nitro/mixin/form.rb +88 -0
- data/lib/nitro/mixin/helper.rb +2 -2
- data/lib/nitro/mixin/javascript.rb +108 -108
- data/lib/nitro/mixin/markup.rb +144 -0
- data/lib/nitro/mixin/pager.rb +202 -202
- data/lib/nitro/mixin/rss.rb +67 -0
- data/lib/nitro/mixin/table.rb +63 -0
- data/lib/nitro/mixin/xhtml.rb +75 -0
- data/lib/nitro/mixin/xml.rb +124 -0
- data/lib/nitro/render.rb +183 -359
- data/lib/nitro/request.rb +140 -140
- data/lib/nitro/response.rb +27 -27
- data/lib/nitro/routing.rb +21 -21
- data/lib/nitro/scaffold.rb +124 -118
- data/lib/nitro/server.rb +117 -80
- data/lib/nitro/server/runner.rb +341 -0
- data/lib/nitro/service.rb +12 -12
- data/lib/nitro/service/xmlrpc.rb +22 -22
- data/lib/nitro/session.rb +122 -120
- data/lib/nitro/session/drb.rb +9 -9
- data/lib/nitro/session/drbserver.rb +34 -34
- data/lib/nitro/template.rb +171 -155
- data/lib/nitro/testing/assertions.rb +90 -90
- data/lib/nitro/testing/context.rb +16 -16
- data/lib/nitro/testing/testcase.rb +34 -34
- data/proto/conf/lhttpd.conf +9 -9
- data/proto/public/error.xhtml +75 -75
- data/proto/public/index.xhtml +18 -18
- data/proto/public/js/behaviour.js +65 -65
- data/proto/public/js/controls.js +1 -1
- data/proto/public/js/prototype.js +3 -3
- data/proto/public/settings.xhtml +61 -61
- data/proto/run.rb +1 -5
- data/test/nitro/adapter/raw_post1.bin +0 -0
- data/test/nitro/adapter/tc_cgi.rb +57 -57
- data/test/nitro/adapter/tc_webrick.rb +4 -4
- data/test/nitro/mixin/tc_pager.rb +25 -25
- data/test/nitro/mixin/tc_rss.rb +24 -0
- data/test/nitro/mixin/tc_table.rb +31 -0
- data/test/nitro/mixin/tc_xhtml.rb +13 -0
- data/test/nitro/tc_caching.rb +10 -10
- data/test/nitro/tc_context.rb +8 -8
- data/test/nitro/tc_controller.rb +48 -48
- data/test/nitro/tc_cookie.rb +6 -6
- data/test/nitro/tc_dispatcher.rb +64 -64
- data/test/nitro/tc_element.rb +27 -27
- data/test/nitro/tc_flash.rb +31 -31
- data/test/nitro/tc_mail.rb +63 -63
- data/test/nitro/tc_server.rb +26 -26
- data/test/nitro/tc_session.rb +9 -9
- data/test/nitro/tc_template.rb +19 -19
- data/test/public/blog/list.xhtml +1 -1
- metadata +31 -37
- data/lib/nitro/buffering.rb +0 -45
- data/lib/nitro/builder/form.rb +0 -104
- data/lib/nitro/builder/rss.rb +0 -104
- data/lib/nitro/builder/table.rb +0 -80
- data/lib/nitro/builder/xhtml.rb +0 -132
- data/lib/nitro/builder/xml.rb +0 -131
- data/lib/nitro/conf.rb +0 -36
- data/lib/nitro/environment.rb +0 -21
- data/lib/nitro/errors.rb +0 -69
- data/lib/nitro/localization.rb +0 -153
- data/lib/nitro/markup.rb +0 -147
- data/lib/nitro/output.rb +0 -24
- data/lib/nitro/runner.rb +0 -348
- data/lib/nitro/shaders.rb +0 -206
- data/test/nitro/builder/tc_rss.rb +0 -23
- data/test/nitro/builder/tc_table.rb +0 -30
- data/test/nitro/builder/tc_xhtml.rb +0 -39
- data/test/nitro/builder/tc_xml.rb +0 -56
- data/test/nitro/tc_localization.rb +0 -49
|
@@ -9,60 +9,60 @@ module Nitro
|
|
|
9
9
|
|
|
10
10
|
module Caching
|
|
11
11
|
|
|
12
|
-
|
|
12
|
+
# Action caching.
|
|
13
13
|
|
|
14
|
-
|
|
14
|
+
module Fragments
|
|
15
15
|
|
|
16
|
-
|
|
16
|
+
@@store = FileStore.new # MemoryStore.new
|
|
17
17
|
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
18
|
+
def self.store
|
|
19
|
+
@@store
|
|
20
|
+
end
|
|
21
|
+
|
|
22
|
+
def self.store=(store)
|
|
23
|
+
@@store = store
|
|
24
|
+
end
|
|
25
|
+
|
|
26
|
+
def self.get(name, options = {})
|
|
27
|
+
return @@store.read(name, options)
|
|
28
|
+
end
|
|
29
|
+
|
|
30
|
+
def self.put(name, content = nil, options = {})
|
|
31
|
+
@@store.write(name, content, options)
|
|
32
|
+
return content
|
|
33
|
+
end
|
|
34
|
+
|
|
35
|
+
def self.append_features(base) # :nodoc:
|
|
36
|
+
super
|
|
37
|
+
end
|
|
38
38
|
|
|
39
|
-
|
|
39
|
+
private
|
|
40
40
|
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
41
|
+
def cache(name = nil, options = {}, &block)
|
|
42
|
+
name = @action_name unless name
|
|
43
|
+
cache_fragment(block, "#{name}#{options}", options)
|
|
44
|
+
end
|
|
45
45
|
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
46
|
+
def cache_fragment(block, name, options = {})
|
|
47
|
+
unless caching_enabled?
|
|
48
|
+
block.call
|
|
49
|
+
return
|
|
50
|
+
end
|
|
51
51
|
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
52
|
+
if fragment = Fragments.get(name, options)
|
|
53
|
+
@out << fragment
|
|
54
|
+
else
|
|
55
|
+
pos = @out.length
|
|
56
|
+
block.call
|
|
57
|
+
Fragments.put(name, @out[pos..-1], options)
|
|
58
|
+
end
|
|
59
|
+
end
|
|
60
60
|
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
61
|
+
def expire_fragment(name, options = {})
|
|
62
|
+
Fragments.store.delete(name, options)
|
|
63
|
+
end
|
|
64
|
+
end
|
|
65
|
+
|
|
66
66
|
end
|
|
67
67
|
|
|
68
68
|
end
|
|
@@ -1,24 +1,24 @@
|
|
|
1
1
|
# * George Moschovitis <gm@navel.gr>
|
|
2
2
|
# (c) 2004-2005 Navel, all rights reserved.
|
|
3
|
-
# $Id: invalidation.rb
|
|
3
|
+
# $Id: invalidation.rb 182 2005-07-22 10:07:50Z gmosx $
|
|
4
4
|
|
|
5
5
|
module Nitro
|
|
6
6
|
|
|
7
7
|
module Caching
|
|
8
8
|
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
9
|
+
# Support for invalidating output/fragment caches.
|
|
10
|
+
|
|
11
|
+
module Invalidation
|
|
12
12
|
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
13
|
+
def self.append_features(base) #:nodoc:
|
|
14
|
+
super
|
|
15
|
+
base.extend(ClassMethods)
|
|
16
|
+
end
|
|
17
17
|
|
|
18
|
-
|
|
19
|
-
|
|
18
|
+
module ClassMethods
|
|
19
|
+
end
|
|
20
20
|
|
|
21
|
-
|
|
21
|
+
end
|
|
22
22
|
|
|
23
23
|
end
|
|
24
24
|
|
data/lib/nitro/caching/output.rb
CHANGED
|
@@ -6,71 +6,71 @@ module Nitro
|
|
|
6
6
|
|
|
7
7
|
module Caching
|
|
8
8
|
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
#
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
9
|
+
# Output caching.
|
|
10
|
+
|
|
11
|
+
module Output
|
|
12
|
+
|
|
13
|
+
def self.append_features(base) # :nodoc:
|
|
14
|
+
super
|
|
15
|
+
base.extend(ClassMethods)
|
|
16
|
+
base.module_eval do
|
|
17
|
+
cattr_accessor :output_cache_root, 'public'
|
|
18
|
+
end
|
|
19
|
+
end
|
|
20
|
+
|
|
21
|
+
module ClassMethods
|
|
22
|
+
|
|
23
|
+
def do_cache_output(path, content)
|
|
24
|
+
filepath = output_cache_path(path)
|
|
25
|
+
FileUtils.makedirs(File.dirname(filepath))
|
|
26
|
+
File.open(filepath, 'w+') { |f| f.write(content) }
|
|
27
|
+
Logger.debug "Cached page: #{filepath}" if $DBG
|
|
28
|
+
end
|
|
29
|
+
|
|
30
|
+
# Enable output caching for the given actions.
|
|
31
|
+
|
|
32
|
+
def cache_output(*actions)
|
|
33
|
+
return unless caching_enabled?
|
|
34
|
+
|
|
35
|
+
str = actions.collect { |a| ":#{a}" }.join(', ')
|
|
36
|
+
|
|
37
|
+
module_eval %{
|
|
38
|
+
post "do_cache_output", :only => [ #{str} ]
|
|
39
|
+
}
|
|
40
|
+
end
|
|
41
|
+
|
|
42
|
+
private
|
|
43
|
+
|
|
44
|
+
def output_cache_path(path)
|
|
45
|
+
filename = ((path.empty? || path == '/') ? '/index' : path.dup)
|
|
46
|
+
# filename.gsub!(/\/$/, '')
|
|
47
|
+
filename << 'index.html' unless (name.split('/').last || name).include? '.'
|
|
48
|
+
return output_cache_root + '/' + filename
|
|
49
|
+
end
|
|
50
|
+
|
|
51
|
+
end
|
|
52
|
+
|
|
53
|
+
private
|
|
54
|
+
|
|
55
|
+
def do_cache_output
|
|
56
|
+
if caching_enabled? and caching_allowed?
|
|
57
|
+
self.class.do_cache_output(@request.uri, @out)
|
|
58
|
+
end
|
|
59
|
+
end
|
|
60
|
+
|
|
61
|
+
def expire_output(name)
|
|
62
|
+
begin
|
|
63
|
+
FileUtils.rm("#{context.dispatcher.public_root}/#{name}/index.html")
|
|
64
|
+
rescue Object
|
|
65
|
+
# gmosx: is this the right thing to do?
|
|
66
|
+
end
|
|
67
|
+
end
|
|
68
|
+
|
|
69
|
+
def caching_allowed?
|
|
70
|
+
!@request.post?
|
|
71
|
+
end
|
|
72
|
+
|
|
73
|
+
end
|
|
74
74
|
|
|
75
75
|
end
|
|
76
76
|
|
data/lib/nitro/caching/stores.rb
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
# * George Moschovitis <gm@navel.gr>
|
|
2
2
|
# (c) 2004-2005 Navel, all rights reserved.
|
|
3
|
-
# $Id: stores.rb
|
|
3
|
+
# $Id: stores.rb 182 2005-07-22 10:07:50Z gmosx $
|
|
4
4
|
|
|
5
5
|
require 'fileutils'
|
|
6
6
|
|
|
@@ -12,72 +12,72 @@ module Nitro
|
|
|
12
12
|
|
|
13
13
|
module Caching
|
|
14
14
|
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
15
|
+
# Cached fragments are stored in memory.
|
|
16
|
+
|
|
17
|
+
class MemoryStore < Glue::SafeHash
|
|
18
|
+
|
|
19
|
+
def read(name, options = {})
|
|
20
|
+
self[name]
|
|
21
|
+
end
|
|
22
|
+
|
|
23
|
+
def write(name, content = '', options = {})
|
|
24
|
+
self[name] = content
|
|
25
|
+
end
|
|
26
|
+
|
|
27
|
+
def delete(name, options = {})
|
|
28
|
+
self.delete(name)
|
|
29
|
+
end
|
|
30
|
+
|
|
31
|
+
end
|
|
32
|
+
|
|
33
|
+
# Cached fragments are stored as html files
|
|
34
|
+
# on the filesystem.
|
|
35
|
+
|
|
36
|
+
class FileStore
|
|
37
|
+
cattr_accessor :cache_root, 'cache'
|
|
38
|
+
|
|
39
|
+
def initialize(cache_root = FileStore.cache_root)
|
|
40
|
+
@cache_root = cache_root
|
|
41
|
+
end
|
|
42
|
+
|
|
43
|
+
def read(name, options = {})
|
|
44
|
+
begin
|
|
45
|
+
IO.read(path_for_name(name))
|
|
46
|
+
rescue
|
|
47
|
+
nil
|
|
48
|
+
end
|
|
49
|
+
end
|
|
50
|
+
|
|
51
|
+
def write(name, content = '', options = {})
|
|
52
|
+
begin
|
|
53
|
+
path = path_for_name(name)
|
|
54
|
+
dir = File.dirname(path)
|
|
55
|
+
FileUtils.makedirs(dir) unless File.exists?(dir)
|
|
56
|
+
|
|
57
|
+
File.open(path, 'w+') { |f| f.write(content) }
|
|
58
|
+
rescue
|
|
59
|
+
Logger.error "Could not save cached file '#{path}'"
|
|
60
|
+
end
|
|
61
|
+
end
|
|
62
|
+
|
|
63
|
+
def delete(name, options = {})
|
|
64
|
+
path = path_for_name(name)
|
|
65
|
+
File.delete(path) if File.exist?(path)
|
|
66
|
+
end
|
|
67
|
+
|
|
68
|
+
private
|
|
69
|
+
|
|
70
|
+
def path_for_name(name)
|
|
71
|
+
"#@cache_root/#{name}"
|
|
72
|
+
end
|
|
73
|
+
|
|
74
|
+
end
|
|
75
|
+
|
|
76
|
+
class DrbStrore
|
|
77
|
+
end
|
|
78
|
+
|
|
79
|
+
class MemcacheStore
|
|
80
|
+
end
|
|
81
81
|
|
|
82
82
|
end
|
|
83
83
|
|
|
@@ -0,0 +1,262 @@
|
|
|
1
|
+
require 'facet/object/singleton_class'
|
|
2
|
+
|
|
3
|
+
require 'nitro/template'
|
|
4
|
+
require 'nitro/compiler/errors'
|
|
5
|
+
|
|
6
|
+
module Nitro
|
|
7
|
+
|
|
8
|
+
# The Compiler transforms published methods (actions) and
|
|
9
|
+
# assorted template files (views) into specialized code that
|
|
10
|
+
# responds to a URI.
|
|
11
|
+
|
|
12
|
+
class Compiler
|
|
13
|
+
unless const_defined? :PROTO_TEMPLATE_ROOT
|
|
14
|
+
PROTO_TEMPLATE_ROOT = "#{Nitro.proto_path}/public"
|
|
15
|
+
end
|
|
16
|
+
|
|
17
|
+
# Set to true to force reloading of code and templates for
|
|
18
|
+
# each request. Extremely useful during development. Must be
|
|
19
|
+
# turned off in production servers to avoid the severe
|
|
20
|
+
# performance penalty.
|
|
21
|
+
|
|
22
|
+
setting :reload, :default => true, :doc => 'If true all code and templates are reloaded in each request'
|
|
23
|
+
|
|
24
|
+
# Action names with double underscores (__) are converted
|
|
25
|
+
# to subdirectories. Here are some example mappings:
|
|
26
|
+
#
|
|
27
|
+
# hello_world -> template_root/hello_world.xhtml
|
|
28
|
+
# this__is__my__hello_world -> template_root/this/is/my/hello_world
|
|
29
|
+
|
|
30
|
+
def template_for_action(action, template_root = Template.root, ext = Template.extension)
|
|
31
|
+
# attempt to find a template of the form
|
|
32
|
+
# template_root/action.xhtml
|
|
33
|
+
|
|
34
|
+
path = "#{template_root}/#{action.gsub(/__/, '/')}.#{ext}".squeeze('/')
|
|
35
|
+
|
|
36
|
+
unless File.exist?(path)
|
|
37
|
+
# attempt to find a template of the form
|
|
38
|
+
# template_root/action/index.xhtml
|
|
39
|
+
|
|
40
|
+
path = "#{template_root}/#{action.gsub(/__/, '/')}/#{Template.default}.#{ext}".squeeze('/')
|
|
41
|
+
|
|
42
|
+
unless File.exist?(path)
|
|
43
|
+
# No template found!
|
|
44
|
+
return nil
|
|
45
|
+
end
|
|
46
|
+
end
|
|
47
|
+
|
|
48
|
+
return path
|
|
49
|
+
end
|
|
50
|
+
|
|
51
|
+
# This is methods transforms the template. Typically
|
|
52
|
+
# template processors are added as aspects to this method
|
|
53
|
+
# to allow for customized template transformation prior
|
|
54
|
+
# to compilation.
|
|
55
|
+
#
|
|
56
|
+
# The default transformation extracts the Ruby code from
|
|
57
|
+
# processing instructions.
|
|
58
|
+
|
|
59
|
+
def transform_template(template)
|
|
60
|
+
Template.transform(template)
|
|
61
|
+
end
|
|
62
|
+
|
|
63
|
+
# Compile the template into a render method.
|
|
64
|
+
|
|
65
|
+
def compile_template(klass, action, path)
|
|
66
|
+
Logger.debug "Compiling template '#{klass}: #{path}'" if $DBG
|
|
67
|
+
|
|
68
|
+
template = File.read(path)
|
|
69
|
+
|
|
70
|
+
code = %{
|
|
71
|
+
def #{action}_template
|
|
72
|
+
#{transform_template(template)}
|
|
73
|
+
end
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
begin
|
|
77
|
+
klass.class_eval(code, path)
|
|
78
|
+
rescue SyntaxError => e
|
|
79
|
+
raise TemplateCompileError.new(code, template, e)
|
|
80
|
+
end
|
|
81
|
+
end
|
|
82
|
+
|
|
83
|
+
# Compiles an action.
|
|
84
|
+
#
|
|
85
|
+
# Passes the action name and the parent action name in the
|
|
86
|
+
# @action_name and @parent_action_name respectively.
|
|
87
|
+
#--
|
|
88
|
+
# TODO: cleanup this method.
|
|
89
|
+
#++
|
|
90
|
+
|
|
91
|
+
def compile_action(klass, action)
|
|
92
|
+
#--
|
|
93
|
+
# gmosx: Move elsewhere.
|
|
94
|
+
#++
|
|
95
|
+
|
|
96
|
+
Aspects.include_advice_modules(klass)
|
|
97
|
+
|
|
98
|
+
action = action.to_s.gsub(/_action$/, '')
|
|
99
|
+
|
|
100
|
+
return false unless action
|
|
101
|
+
|
|
102
|
+
Logger.debug "Compiling action '#{klass}##{action}'" if $DBG
|
|
103
|
+
|
|
104
|
+
valid = false
|
|
105
|
+
|
|
106
|
+
code = %{
|
|
107
|
+
def #{action}_action
|
|
108
|
+
@parent_action_name = @action_name
|
|
109
|
+
@action_name = '#{action}'
|
|
110
|
+
}
|
|
111
|
+
|
|
112
|
+
# Inject the pre advices.
|
|
113
|
+
|
|
114
|
+
code << Aspects.gen_advice_code(action, klass.advices, :pre)
|
|
115
|
+
|
|
116
|
+
# Call the action
|
|
117
|
+
|
|
118
|
+
if klass.action_methods.include?(action)
|
|
119
|
+
valid = true
|
|
120
|
+
|
|
121
|
+
# Annotated parameters.
|
|
122
|
+
|
|
123
|
+
if meta = klass.action_metadata[action.intern]
|
|
124
|
+
params = meta.params.keys
|
|
125
|
+
params = params.collect { |p| "@#{p} = @context['#{p}']" }
|
|
126
|
+
code << "#{params.join(';')}"
|
|
127
|
+
end
|
|
128
|
+
|
|
129
|
+
# Try to resolve action parameters.
|
|
130
|
+
|
|
131
|
+
param_count = klass.instance_method(action.intern).arity
|
|
132
|
+
|
|
133
|
+
# gmosx, FIXME: REIMPLEMENT THIS!!!!
|
|
134
|
+
|
|
135
|
+
if param_count > 0
|
|
136
|
+
code << %{
|
|
137
|
+
params = []
|
|
138
|
+
qs = context.query_string.split(/[&;]/)
|
|
139
|
+
|
|
140
|
+
#{param_count}.times do |i|
|
|
141
|
+
params << qs.shift.split(/=/).last
|
|
142
|
+
end
|
|
143
|
+
|
|
144
|
+
action_return_value = #{action}(*params)
|
|
145
|
+
}
|
|
146
|
+
else
|
|
147
|
+
code << %{
|
|
148
|
+
action_return_value = #{action}
|
|
149
|
+
}
|
|
150
|
+
end
|
|
151
|
+
code << %{
|
|
152
|
+
unless :stop == action_return_value
|
|
153
|
+
}
|
|
154
|
+
end
|
|
155
|
+
|
|
156
|
+
# Try to call the template method if it exists. It is a
|
|
157
|
+
# nice practice to put output related code in this method
|
|
158
|
+
# instead of the main action so that this method can be
|
|
159
|
+
# overloaded separately.
|
|
160
|
+
#
|
|
161
|
+
# If no template method exists, try to convert an external
|
|
162
|
+
# template file into a template method. It is an even
|
|
163
|
+
# better practice to place the output related code in an
|
|
164
|
+
# external template file.
|
|
165
|
+
|
|
166
|
+
# Take :view metadata into account.
|
|
167
|
+
|
|
168
|
+
view = nil
|
|
169
|
+
if md = klass.action_metadata[action.intern]
|
|
170
|
+
view = md[:view]
|
|
171
|
+
end
|
|
172
|
+
view ||= action
|
|
173
|
+
|
|
174
|
+
cklass = klass
|
|
175
|
+
template_path = nil
|
|
176
|
+
|
|
177
|
+
loop do
|
|
178
|
+
template_root = nil
|
|
179
|
+
|
|
180
|
+
if cklass.respond_to?(:template_root)
|
|
181
|
+
template_root = cklass.template_root
|
|
182
|
+
end
|
|
183
|
+
|
|
184
|
+
# Don't use a proto template if there is an action
|
|
185
|
+
# defined.
|
|
186
|
+
|
|
187
|
+
template_root ||= PROTO_TEMPLATE_ROOT unless valid
|
|
188
|
+
|
|
189
|
+
if template_root and template_path = template_for_action(view.to_s, template_root)
|
|
190
|
+
valid = true
|
|
191
|
+
code << %{
|
|
192
|
+
#{action}_template;
|
|
193
|
+
}
|
|
194
|
+
break
|
|
195
|
+
end
|
|
196
|
+
|
|
197
|
+
break unless cklass = cklass.superclass
|
|
198
|
+
end
|
|
199
|
+
|
|
200
|
+
return false unless valid
|
|
201
|
+
|
|
202
|
+
if klass.action_methods.include?(action)
|
|
203
|
+
code << %{
|
|
204
|
+
if @out.empty? and action_return_value.is_a?(String)
|
|
205
|
+
print(action_return_value)
|
|
206
|
+
end
|
|
207
|
+
end
|
|
208
|
+
}
|
|
209
|
+
end
|
|
210
|
+
|
|
211
|
+
if Render.redirect_on_empty
|
|
212
|
+
code << %{
|
|
213
|
+
redirect_referer if @out.empty?
|
|
214
|
+
}
|
|
215
|
+
end
|
|
216
|
+
|
|
217
|
+
# Inject the post advices.
|
|
218
|
+
|
|
219
|
+
code << Aspects.gen_advice_code(action, klass.advices, :post)
|
|
220
|
+
|
|
221
|
+
code << %{
|
|
222
|
+
@action_name = @parent_action_name
|
|
223
|
+
end
|
|
224
|
+
}
|
|
225
|
+
|
|
226
|
+
# First compile the action method.
|
|
227
|
+
|
|
228
|
+
# begin
|
|
229
|
+
klass.class_eval(code)
|
|
230
|
+
# rescue SyntaxError => e
|
|
231
|
+
# raise ActionCompileError.new(code, action, e)
|
|
232
|
+
# end
|
|
233
|
+
|
|
234
|
+
compile_template(klass, action, template_path) if template_path
|
|
235
|
+
|
|
236
|
+
return true
|
|
237
|
+
|
|
238
|
+
end
|
|
239
|
+
|
|
240
|
+
# Compiles an action method in the given (controller) class.
|
|
241
|
+
# A sync is used to make compilation thread safe.
|
|
242
|
+
|
|
243
|
+
def compile(klass, action)
|
|
244
|
+
compile_action(klass, action)
|
|
245
|
+
end
|
|
246
|
+
|
|
247
|
+
# :section: Helper methods.
|
|
248
|
+
|
|
249
|
+
class << self
|
|
250
|
+
# Helper method for manipulating the template transformation
|
|
251
|
+
# pipeline.
|
|
252
|
+
|
|
253
|
+
def setup_transform_template(&block)
|
|
254
|
+
send :define_method, :transform_template, block
|
|
255
|
+
end
|
|
256
|
+
alias_method :setup_template_transform, :setup_transform_template
|
|
257
|
+
alias_method :setup_template_transformation, :setup_transform_template
|
|
258
|
+
end
|
|
259
|
+
|
|
260
|
+
end
|
|
261
|
+
|
|
262
|
+
end
|