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
|
@@ -0,0 +1,63 @@
|
|
|
1
|
+
module Nitro
|
|
2
|
+
|
|
3
|
+
# The TableBuilder is a helper class that automates the creation
|
|
4
|
+
# of tables from collections of objects. The resulting html
|
|
5
|
+
# can be styled using css.
|
|
6
|
+
#
|
|
7
|
+
# === Example
|
|
8
|
+
#
|
|
9
|
+
# <?r
|
|
10
|
+
# users = User.all.map { |u| [u.name, u.first_name, u.last_name, u.email] }
|
|
11
|
+
# header = ['Username', 'First name', 'Last name', 'Email']
|
|
12
|
+
# ?>
|
|
13
|
+
#
|
|
14
|
+
# <div class="custom-table-class">
|
|
15
|
+
# #{table(users, header)}
|
|
16
|
+
# </div>
|
|
17
|
+
#--
|
|
18
|
+
# TODO: sorting, thead/tbody/legend etc, verbose...
|
|
19
|
+
#++
|
|
20
|
+
|
|
21
|
+
module TableMixin
|
|
22
|
+
|
|
23
|
+
# [+options+]
|
|
24
|
+
# A hash of options.
|
|
25
|
+
#
|
|
26
|
+
# :id = id of the component.
|
|
27
|
+
# :headers = an array of the header values
|
|
28
|
+
# :values = an array of arrays.
|
|
29
|
+
|
|
30
|
+
def table(options)
|
|
31
|
+
c = options
|
|
32
|
+
|
|
33
|
+
str = '<table'
|
|
34
|
+
str << %| id="#{c[:id]}"| if c[:id]
|
|
35
|
+
str << '><tr>'
|
|
36
|
+
|
|
37
|
+
for h in c[:headers]
|
|
38
|
+
str << %|<th>#{h}</th>|
|
|
39
|
+
end
|
|
40
|
+
|
|
41
|
+
str << "</tr>"
|
|
42
|
+
|
|
43
|
+
for row in c[:values]
|
|
44
|
+
str << "<tr>"
|
|
45
|
+
|
|
46
|
+
for v in row
|
|
47
|
+
str << %|<td>#{v}</td>|
|
|
48
|
+
end
|
|
49
|
+
|
|
50
|
+
str << "</tr>"
|
|
51
|
+
end
|
|
52
|
+
|
|
53
|
+
str << "</table>"
|
|
54
|
+
|
|
55
|
+
return str
|
|
56
|
+
end
|
|
57
|
+
alias_method :build_table, :table
|
|
58
|
+
|
|
59
|
+
end
|
|
60
|
+
|
|
61
|
+
end
|
|
62
|
+
|
|
63
|
+
# * George Moschovitis <gm@navel.gr>
|
|
@@ -0,0 +1,75 @@
|
|
|
1
|
+
module Nitro
|
|
2
|
+
|
|
3
|
+
# A helper mixin for programmatically building XHTML
|
|
4
|
+
# blocks.
|
|
5
|
+
|
|
6
|
+
module XhtmlMixin
|
|
7
|
+
|
|
8
|
+
# Render select options. The parameter is a hash of options.
|
|
9
|
+
#
|
|
10
|
+
# [+labels+]
|
|
11
|
+
# The option labels.
|
|
12
|
+
#
|
|
13
|
+
# [+values+]
|
|
14
|
+
# The corresponding values.
|
|
15
|
+
#
|
|
16
|
+
# [+selected+]
|
|
17
|
+
# The value of the selected option.
|
|
18
|
+
#
|
|
19
|
+
# === Examples
|
|
20
|
+
#
|
|
21
|
+
# labels = ['Male', 'Female']
|
|
22
|
+
# o.select(:name => 'sex') {
|
|
23
|
+
# o.options(:labels => labels, :selected => 1)
|
|
24
|
+
# }
|
|
25
|
+
#
|
|
26
|
+
# or
|
|
27
|
+
#
|
|
28
|
+
# #{options :labels => labels, :values => [..], :selected => 1}
|
|
29
|
+
# #{build :options, :labels => labels, :values => [..], :selected => 1}
|
|
30
|
+
|
|
31
|
+
def options(options = {})
|
|
32
|
+
if labels = options[:labels]
|
|
33
|
+
str = ''
|
|
34
|
+
|
|
35
|
+
unless values = options[:values]
|
|
36
|
+
values = (0...labels.size).to_a
|
|
37
|
+
end
|
|
38
|
+
|
|
39
|
+
selected = (options[:selected] || -1).to_i
|
|
40
|
+
|
|
41
|
+
labels.each_with_index do |label, idx|
|
|
42
|
+
value = values[idx]
|
|
43
|
+
if value == selected
|
|
44
|
+
str << %|<option value="#{value}" selected="1">#{label}</option>|
|
|
45
|
+
else
|
|
46
|
+
str << %|<option value="#{value}">#{label}</option>|
|
|
47
|
+
end
|
|
48
|
+
end
|
|
49
|
+
|
|
50
|
+
return str
|
|
51
|
+
else
|
|
52
|
+
raise ArgumentError.new('No labels provided')
|
|
53
|
+
end
|
|
54
|
+
end
|
|
55
|
+
|
|
56
|
+
# Render a submit input.
|
|
57
|
+
|
|
58
|
+
def submit(options = nil)
|
|
59
|
+
str = ''
|
|
60
|
+
|
|
61
|
+
if options
|
|
62
|
+
opts = options.collect { |k, v| %[#{k}="#{v}"] }.join(' ')
|
|
63
|
+
str << %[<input type="submit" #{opts} />]
|
|
64
|
+
else
|
|
65
|
+
str << %|<input type="submit" />|
|
|
66
|
+
end
|
|
67
|
+
|
|
68
|
+
return str
|
|
69
|
+
end
|
|
70
|
+
|
|
71
|
+
end
|
|
72
|
+
|
|
73
|
+
end
|
|
74
|
+
|
|
75
|
+
# * George Moschovitis <gm@navel.gr>
|
|
@@ -0,0 +1,124 @@
|
|
|
1
|
+
module Nitro
|
|
2
|
+
|
|
3
|
+
# A helper mixin for programmatically building XML
|
|
4
|
+
# blocks.
|
|
5
|
+
|
|
6
|
+
module XmlMixin
|
|
7
|
+
|
|
8
|
+
def method_missing(tag, *args, &block)
|
|
9
|
+
self.class.module_eval <<-"end_eval", __FILE__, __LINE__
|
|
10
|
+
def #{tag}(*args)
|
|
11
|
+
attrs = args.last.is_a?(Hash) ? args.pop : nil
|
|
12
|
+
|
|
13
|
+
if block_given?
|
|
14
|
+
start_tag!('#{tag}', attrs)
|
|
15
|
+
yield
|
|
16
|
+
end_tag!('#{tag}')
|
|
17
|
+
elsif (!args.empty?)
|
|
18
|
+
start_tag!('#{tag}', attrs)
|
|
19
|
+
self << args.first
|
|
20
|
+
end_tag!('#{tag}')
|
|
21
|
+
else
|
|
22
|
+
start_tag!('#{tag}', attrs, false)
|
|
23
|
+
self << ' />'
|
|
24
|
+
end
|
|
25
|
+
end
|
|
26
|
+
end_eval
|
|
27
|
+
|
|
28
|
+
self.send(tag, *args, &block)
|
|
29
|
+
end
|
|
30
|
+
|
|
31
|
+
# Emit the start (opening) tag of an element.
|
|
32
|
+
|
|
33
|
+
def start_tag!(tag, attributes = nil, close = true)
|
|
34
|
+
unless attributes
|
|
35
|
+
if close
|
|
36
|
+
self << "<#{tag}>"
|
|
37
|
+
else
|
|
38
|
+
self << "<#{tag}"
|
|
39
|
+
end
|
|
40
|
+
else
|
|
41
|
+
self << "<#{tag}"
|
|
42
|
+
for name, value in attributes
|
|
43
|
+
if value
|
|
44
|
+
self << %| #{name}="#{value}"|
|
|
45
|
+
else
|
|
46
|
+
self << %| #{name}="1"|
|
|
47
|
+
end
|
|
48
|
+
end
|
|
49
|
+
self << ">" if close
|
|
50
|
+
end
|
|
51
|
+
|
|
52
|
+
return self
|
|
53
|
+
end
|
|
54
|
+
|
|
55
|
+
# Emit the end (closing) tag of an element.
|
|
56
|
+
|
|
57
|
+
def end_tag!(tag)
|
|
58
|
+
self << "</#{tag}>"
|
|
59
|
+
|
|
60
|
+
return self
|
|
61
|
+
end
|
|
62
|
+
|
|
63
|
+
# Emit a text string.
|
|
64
|
+
|
|
65
|
+
def text!(str)
|
|
66
|
+
self << str
|
|
67
|
+
|
|
68
|
+
return self
|
|
69
|
+
end
|
|
70
|
+
alias_method :print, :text!
|
|
71
|
+
|
|
72
|
+
# Emit a comment.
|
|
73
|
+
|
|
74
|
+
def comment!(str)
|
|
75
|
+
self << "<!-- #{str} -->"
|
|
76
|
+
|
|
77
|
+
return self
|
|
78
|
+
end
|
|
79
|
+
|
|
80
|
+
# Emit a processing instruction.
|
|
81
|
+
|
|
82
|
+
def processing_instruction!(name, attributes = nil)
|
|
83
|
+
unless attributes
|
|
84
|
+
self << "<?#{name} ?>"
|
|
85
|
+
else
|
|
86
|
+
self << "<?#{name} "
|
|
87
|
+
attributes.each do |a, v|
|
|
88
|
+
self << %[#{a}="#{v}" ]
|
|
89
|
+
end
|
|
90
|
+
self << "?>"
|
|
91
|
+
end
|
|
92
|
+
end
|
|
93
|
+
alias_method :pi!, :processing_instruction!
|
|
94
|
+
|
|
95
|
+
end
|
|
96
|
+
|
|
97
|
+
# A class that encapsulats the XML generation
|
|
98
|
+
# functionality. Utilizes duck typing to redirect
|
|
99
|
+
# output to a target buffer.
|
|
100
|
+
|
|
101
|
+
class XmlBuilder
|
|
102
|
+
include XmlMixin
|
|
103
|
+
|
|
104
|
+
# The target receives the generated xml,
|
|
105
|
+
# should respond_to :<<
|
|
106
|
+
|
|
107
|
+
attr_accessor :target
|
|
108
|
+
|
|
109
|
+
def initialize(target = '')
|
|
110
|
+
@target = target
|
|
111
|
+
end
|
|
112
|
+
|
|
113
|
+
def << (str)
|
|
114
|
+
@target << str
|
|
115
|
+
end
|
|
116
|
+
|
|
117
|
+
def to_s
|
|
118
|
+
@target.to_s
|
|
119
|
+
end
|
|
120
|
+
end
|
|
121
|
+
|
|
122
|
+
end
|
|
123
|
+
|
|
124
|
+
# * George Moschovitis <gm@navel.gr>
|
data/lib/nitro/render.rb
CHANGED
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
require 'singleton'
|
|
1
2
|
require 'sync'
|
|
2
3
|
|
|
3
4
|
require 'facet/string/blank%3F'
|
|
@@ -6,388 +7,211 @@ require 'glue/attribute'
|
|
|
6
7
|
require 'glue/misc'
|
|
7
8
|
require 'glue/object'
|
|
8
9
|
require 'glue/settings'
|
|
10
|
+
require 'glue/builder'
|
|
11
|
+
require 'glue/builder/xml'
|
|
9
12
|
|
|
10
|
-
require 'nitro/
|
|
11
|
-
require 'nitro/
|
|
12
|
-
require 'nitro/
|
|
13
|
+
require 'nitro/mixin/xhtml'
|
|
14
|
+
require 'nitro/mixin/form'
|
|
15
|
+
require 'nitro/mixin/table'
|
|
16
|
+
require 'nitro/mixin/buffer'
|
|
13
17
|
|
|
14
18
|
module Nitro
|
|
15
19
|
|
|
16
|
-
# Raise this exception to stop the current action.
|
|
20
|
+
# Raise or Throw this exception to stop the current action.
|
|
17
21
|
# Typically called to skip the template.
|
|
18
22
|
|
|
19
23
|
class ActionExit < Exception; end
|
|
20
|
-
|
|
21
|
-
# Raise this exception to stop rendering altogether.
|
|
24
|
+
|
|
25
|
+
# Raise or Thorw this exception to stop rendering altogether.
|
|
22
26
|
# Typically called by redirects.
|
|
23
27
|
|
|
24
28
|
class RenderExit < Exception; end
|
|
25
29
|
|
|
26
|
-
#
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
@@sync = Sync.new
|
|
31
|
-
|
|
32
|
-
# The default template root
|
|
33
|
-
|
|
34
|
-
setting :default_template_root, :default => 'public', :doc => 'The default template root'
|
|
35
|
-
|
|
36
|
-
# The default template name (no extension).
|
|
37
|
-
|
|
38
|
-
setting :default_template, :default => 'index', :doc => 'The default template name (no extension).'
|
|
39
|
-
|
|
40
|
-
# The shader used for transforming templates.
|
|
41
|
-
# The default shader is very simple, here is a
|
|
42
|
-
# typical example of a production shader pipeline:
|
|
43
|
-
#
|
|
44
|
-
# <tt>
|
|
45
|
-
# Rendering.shader =
|
|
46
|
-
# XSLTShader.new("xsl/style.xsl",
|
|
47
|
-
# RubyShader.new(
|
|
48
|
-
# CompressShader.new
|
|
49
|
-
# )
|
|
50
|
-
# )
|
|
51
|
-
# </tt>
|
|
52
|
-
|
|
53
|
-
mattr_accessor :shader; @@shader = RubyShader.new
|
|
54
|
-
# setting :shader, :default => RubyShader.new, :doc => 'The shader used for transforming templates'
|
|
55
|
-
|
|
56
|
-
# If set to :full, reloads all controllers. Useful in
|
|
57
|
-
# development.
|
|
58
|
-
|
|
59
|
-
setting :reload, :default => false, :doc => 'If set to :full reloads everything, useful in development'
|
|
60
|
-
|
|
61
|
-
# Given the action try find the matching template.
|
|
62
|
-
# Can search for xhtml or xml templates.
|
|
63
|
-
# Returns nil if no template file is found.
|
|
64
|
-
|
|
65
|
-
def self.template_for_action(template_root, action, ext = :xhtml)
|
|
66
|
-
# attempt to find a template of the form
|
|
67
|
-
# template_root/action.xhtml
|
|
68
|
-
|
|
69
|
-
path = "#{template_root}/#{action.gsub(/__/, '/')}.#{ext}".squeeze('/')
|
|
70
|
-
|
|
71
|
-
unless File.exist?(path)
|
|
72
|
-
# attempt to find a template of the form
|
|
73
|
-
# template_root/action/index.xhtml
|
|
74
|
-
|
|
75
|
-
path = "#{template_root}/#{action.gsub(/__/, '/')}/#{Rendering.default_template}.#{ext}".squeeze('/')
|
|
76
|
-
|
|
77
|
-
unless File.exist?(path)
|
|
78
|
-
# No template found!
|
|
79
|
-
path = nil
|
|
80
|
-
end
|
|
81
|
-
end
|
|
82
|
-
|
|
83
|
-
return path
|
|
84
|
-
end
|
|
85
|
-
|
|
86
|
-
# Transform a template to ruby rendering code.
|
|
87
|
-
|
|
88
|
-
def self.transform_template(path, shader)
|
|
89
|
-
Logger.debug "Transforming '#{path}'" if $DBG
|
|
90
|
-
|
|
91
|
-
text = File.read(path)
|
|
92
|
-
hash, text = shader.process(path, text)
|
|
93
|
-
|
|
94
|
-
return text
|
|
95
|
-
end
|
|
96
|
-
|
|
97
|
-
# Compile a controller action.
|
|
98
|
-
|
|
99
|
-
def self.compile_action(klass, action)
|
|
100
|
-
@@sync.synchronize do
|
|
101
|
-
Aspects.include_advice_modules(klass)
|
|
102
|
-
|
|
103
|
-
action = action.to_s.gsub(/_action$/, '')
|
|
104
|
-
|
|
105
|
-
# This is not a controller action.
|
|
106
|
-
|
|
107
|
-
return false unless action
|
|
108
|
-
|
|
109
|
-
Logger.debug "Compiling action '#{klass.template_root}/#{action}'" if $DBG
|
|
110
|
-
|
|
111
|
-
valid = false
|
|
112
|
-
|
|
113
|
-
code = %{
|
|
114
|
-
def #{action}_action
|
|
115
|
-
@parent_action_name = @action_name
|
|
116
|
-
@action_name = '#{action}'
|
|
117
|
-
}
|
|
118
|
-
|
|
119
|
-
# Inject the pre advices.
|
|
120
|
-
|
|
121
|
-
code << Aspects.gen_advice_code(action, klass.advices, :pre)
|
|
122
|
-
|
|
123
|
-
# Call the action
|
|
124
|
-
|
|
125
|
-
if klass.action_methods.include?(action)
|
|
126
|
-
valid = true
|
|
127
|
-
|
|
128
|
-
# Annotated parameters.
|
|
129
|
-
|
|
130
|
-
if meta = klass.action_metadata[action.intern]
|
|
131
|
-
params = meta.params.keys
|
|
132
|
-
params = params.collect { |p| "@#{p} = @context['#{p}']" }
|
|
133
|
-
code << "#{params.join(';')}"
|
|
134
|
-
end
|
|
135
|
-
|
|
136
|
-
# Try to resolve action parameters.
|
|
137
|
-
|
|
138
|
-
param_count = klass.instance_method(action.intern).arity
|
|
139
|
-
|
|
140
|
-
# gmosx, FIXME: REIMPLEMENT THIS!!!!
|
|
141
|
-
|
|
142
|
-
if param_count > 0
|
|
143
|
-
code << %{
|
|
144
|
-
params = []
|
|
145
|
-
qs = context.query_string.split(/[&;]/)
|
|
146
|
-
|
|
147
|
-
#{param_count}.times do |i|
|
|
148
|
-
params << qs.shift.split(/=/).last
|
|
149
|
-
end
|
|
150
|
-
|
|
151
|
-
unless :stop == #{action}(*params)
|
|
152
|
-
}
|
|
153
|
-
else
|
|
154
|
-
code << %{
|
|
155
|
-
unless :stop == #{action}
|
|
156
|
-
}
|
|
157
|
-
end
|
|
158
|
-
end
|
|
159
|
-
|
|
160
|
-
# Try to call the template method if it exists. It is a nice
|
|
161
|
-
# practice to put output related code in this method instead
|
|
162
|
-
# of the main action so that this method can be overloaded
|
|
163
|
-
# separately.
|
|
164
|
-
#
|
|
165
|
-
# If no template method exists, try to convert an external
|
|
166
|
-
# template file into a template method. It is an even better
|
|
167
|
-
# practice to place the output related code in an external
|
|
168
|
-
# template file.
|
|
169
|
-
|
|
170
|
-
# Take :view metadata into account.
|
|
171
|
-
|
|
172
|
-
view = nil
|
|
173
|
-
if md = klass.action_metadata[action.intern]
|
|
174
|
-
view = md[:view]
|
|
175
|
-
end
|
|
176
|
-
view ||= action
|
|
177
|
-
|
|
178
|
-
cklass = klass
|
|
179
|
-
|
|
180
|
-
while cklass.respond_to?(:template_root)
|
|
181
|
-
if template = template_for_action(cklass.template_root, view.to_s)
|
|
182
|
-
valid = true
|
|
183
|
-
code << %{
|
|
184
|
-
#{action}_template;
|
|
185
|
-
}
|
|
186
|
-
break
|
|
187
|
-
end
|
|
188
|
-
|
|
189
|
-
# don't search in parent template roots if an
|
|
190
|
-
# action is defined.
|
|
191
|
-
|
|
192
|
-
break if valid
|
|
193
|
-
|
|
194
|
-
cklass = cklass.superclass
|
|
195
|
-
end
|
|
196
|
-
|
|
197
|
-
# raise "Invalid action '#{action}' for '#{klass}'!" unless valid
|
|
198
|
-
return false unless valid
|
|
199
|
-
|
|
200
|
-
# Inject the post advices.
|
|
201
|
-
|
|
202
|
-
code << Aspects.gen_advice_code(action, klass.advices, :post)
|
|
203
|
-
|
|
204
|
-
if klass.action_methods.include?(action)
|
|
205
|
-
code << %{
|
|
206
|
-
end
|
|
207
|
-
}
|
|
208
|
-
end
|
|
209
|
-
|
|
210
|
-
code << %{
|
|
211
|
-
@action_name = @parent_action_name
|
|
212
|
-
redirect_referer if @out.empty?
|
|
213
|
-
end
|
|
214
|
-
}
|
|
215
|
-
|
|
216
|
-
# First compile the action method.
|
|
217
|
-
|
|
218
|
-
# begin
|
|
219
|
-
klass.class_eval(code)
|
|
220
|
-
# rescue SyntaxError => e
|
|
221
|
-
# raise ActionCompileError.new(code, action, e)
|
|
222
|
-
# end
|
|
223
|
-
|
|
224
|
-
# Try to compile the template (if exists).
|
|
225
|
-
|
|
226
|
-
if template
|
|
227
|
-
code = %{
|
|
228
|
-
def #{action}_template
|
|
229
|
-
#{transform_template(template, Rendering.shader)}
|
|
230
|
-
end
|
|
231
|
-
}
|
|
232
|
-
|
|
233
|
-
begin
|
|
234
|
-
klass.class_eval(code, template)
|
|
235
|
-
rescue SyntaxError => e
|
|
236
|
-
raise TemplateCompileError.new(code, template, e)
|
|
237
|
-
end
|
|
238
|
-
end
|
|
239
|
-
end
|
|
240
|
-
|
|
241
|
-
return true
|
|
242
|
-
end
|
|
30
|
+
# The output buffer.
|
|
31
|
+
#--
|
|
32
|
+
# TODO: Implement a FAST string (maybe in C)
|
|
33
|
+
#++
|
|
243
34
|
|
|
35
|
+
class OutputBuffer < String
|
|
244
36
|
end
|
|
245
37
|
|
|
246
|
-
# The rendering mixin.
|
|
38
|
+
# The rendering mixin. This module is typically included in
|
|
39
|
+
# published objects and/or controllers to provide rendering
|
|
40
|
+
# functionality.
|
|
247
41
|
#--
|
|
248
42
|
# TODO: handle template_root here instead of the
|
|
249
43
|
# controller.
|
|
250
44
|
#++
|
|
251
45
|
|
|
252
46
|
module Render
|
|
253
|
-
|
|
254
|
-
|
|
255
|
-
|
|
256
|
-
|
|
257
|
-
|
|
258
|
-
|
|
259
|
-
|
|
260
|
-
|
|
261
|
-
|
|
262
|
-
|
|
263
|
-
|
|
264
|
-
|
|
265
|
-
|
|
266
|
-
|
|
267
|
-
|
|
268
|
-
|
|
269
|
-
|
|
270
|
-
|
|
271
|
-
|
|
272
|
-
|
|
273
|
-
|
|
274
|
-
|
|
275
|
-
|
|
276
|
-
|
|
277
|
-
|
|
278
|
-
|
|
279
|
-
|
|
280
|
-
|
|
281
|
-
|
|
282
|
-
|
|
283
|
-
|
|
284
|
-
|
|
285
|
-
|
|
286
|
-
|
|
287
|
-
|
|
288
|
-
|
|
289
|
-
|
|
290
|
-
|
|
291
|
-
|
|
292
|
-
|
|
293
|
-
|
|
294
|
-
|
|
295
|
-
|
|
296
|
-
|
|
297
|
-
|
|
298
|
-
|
|
299
|
-
|
|
300
|
-
|
|
301
|
-
|
|
302
|
-
|
|
303
|
-
|
|
304
|
-
|
|
305
|
-
|
|
306
|
-
|
|
307
|
-
|
|
308
|
-
|
|
309
|
-
|
|
310
|
-
|
|
311
|
-
|
|
312
|
-
|
|
313
|
-
|
|
314
|
-
|
|
315
|
-
|
|
316
|
-
|
|
317
|
-
|
|
318
|
-
|
|
319
|
-
|
|
320
|
-
|
|
321
|
-
|
|
322
|
-
|
|
323
|
-
|
|
324
|
-
|
|
325
|
-
|
|
326
|
-
|
|
327
|
-
|
|
328
|
-
|
|
329
|
-
|
|
47
|
+
include BufferMixin
|
|
48
|
+
|
|
49
|
+
# If true, auto redirect to referer on empty buffer.
|
|
50
|
+
|
|
51
|
+
setting :redirect_on_empty, :default => false, :doc => 'If true, auto redirect to referer on empty buffer'
|
|
52
|
+
|
|
53
|
+
# The output buffer. The output of a script/action is
|
|
54
|
+
# accumulated in this buffer.
|
|
55
|
+
|
|
56
|
+
attr_accessor :out
|
|
57
|
+
|
|
58
|
+
alias_method :body, :out
|
|
59
|
+
|
|
60
|
+
# The context.
|
|
61
|
+
|
|
62
|
+
attr_accessor :context
|
|
63
|
+
alias_method :ctx, :context
|
|
64
|
+
alias_method :ctx=, :context=
|
|
65
|
+
|
|
66
|
+
# Aliases for context.
|
|
67
|
+
|
|
68
|
+
attr_accessor :request, :response
|
|
69
|
+
|
|
70
|
+
# An array holding the rendering errors for this
|
|
71
|
+
# request.
|
|
72
|
+
|
|
73
|
+
attr_accessor :rendering_errors
|
|
74
|
+
|
|
75
|
+
# The name of the currently executing action.
|
|
76
|
+
|
|
77
|
+
attr_accessor :action_name
|
|
78
|
+
|
|
79
|
+
# The name of the current controller.
|
|
80
|
+
|
|
81
|
+
attr_accessor :controller_name
|
|
82
|
+
|
|
83
|
+
# Initialize the render.
|
|
84
|
+
#
|
|
85
|
+
# [+context+]
|
|
86
|
+
# A parent render/controller acts as the context.
|
|
87
|
+
|
|
88
|
+
def initialize(context, base = nil)
|
|
89
|
+
@request = @response = @context = context
|
|
90
|
+
@controller_name = base
|
|
91
|
+
@out = context.out
|
|
92
|
+
end
|
|
93
|
+
|
|
94
|
+
# Renders the action denoted by path. The path
|
|
95
|
+
# is resolved by the dispatcher to get the correct
|
|
96
|
+
# controller.
|
|
97
|
+
|
|
98
|
+
def render(path)
|
|
99
|
+
Logger.debug "Rendering '#{path}'." if $DBG
|
|
100
|
+
|
|
101
|
+
klass, action, base = @context.dispatcher.dispatch(path, @context)
|
|
102
|
+
|
|
103
|
+
# FIXME:
|
|
104
|
+
@context.content_type = klass.instance_variable_get('@content_type') || 'text/html'
|
|
105
|
+
|
|
106
|
+
raise 'No controller for action' unless klass
|
|
107
|
+
|
|
108
|
+
if self.class == klass
|
|
109
|
+
self.send(action)
|
|
110
|
+
else
|
|
111
|
+
klass.new(self, base).send(action)
|
|
112
|
+
end
|
|
113
|
+
|
|
114
|
+
rescue RenderExit => e
|
|
115
|
+
|
|
116
|
+
# Just stop rendering.
|
|
117
|
+
# For example called by redirects.
|
|
118
|
+
|
|
119
|
+
rescue Exception, StandardError => e
|
|
120
|
+
log_error(e, path)
|
|
121
|
+
|
|
122
|
+
# More fault tolerant, only flags the erroneous box with
|
|
123
|
+
# error not the full page.
|
|
124
|
+
|
|
125
|
+
@out << '(error)'
|
|
126
|
+
end
|
|
330
127
|
|
|
331
128
|
private
|
|
332
129
|
|
|
333
|
-
|
|
334
|
-
|
|
335
|
-
|
|
336
|
-
|
|
337
|
-
|
|
338
|
-
|
|
339
|
-
|
|
340
|
-
|
|
341
|
-
|
|
342
|
-
|
|
343
|
-
|
|
344
|
-
|
|
345
|
-
|
|
346
|
-
|
|
347
|
-
|
|
348
|
-
|
|
349
|
-
|
|
350
|
-
|
|
351
|
-
|
|
352
|
-
|
|
353
|
-
|
|
354
|
-
|
|
355
|
-
|
|
356
|
-
|
|
357
|
-
|
|
358
|
-
|
|
359
|
-
|
|
360
|
-
|
|
361
|
-
|
|
362
|
-
|
|
363
|
-
|
|
364
|
-
|
|
365
|
-
|
|
366
|
-
|
|
367
|
-
|
|
368
|
-
|
|
369
|
-
|
|
370
|
-
|
|
371
|
-
|
|
372
|
-
|
|
373
|
-
|
|
374
|
-
|
|
375
|
-
|
|
376
|
-
|
|
377
|
-
|
|
378
|
-
|
|
379
|
-
|
|
380
|
-
|
|
381
|
-
|
|
382
|
-
|
|
383
|
-
|
|
384
|
-
|
|
385
|
-
|
|
386
|
-
|
|
387
|
-
|
|
388
|
-
|
|
389
|
-
|
|
390
|
-
|
|
130
|
+
# Send a redirect response.
|
|
131
|
+
|
|
132
|
+
def redirect(url, status = 303)
|
|
133
|
+
url = url.to_s
|
|
134
|
+
url = "#{@context.host_url}/#{url.gsub(/^\//, '')}" unless url =~ /http/
|
|
135
|
+
|
|
136
|
+
@context.status = status
|
|
137
|
+
@context.out = "<html><a href=\"#{url}\">#{url}</a>.</html>\n"
|
|
138
|
+
@context.response_headers['location'] = url
|
|
139
|
+
|
|
140
|
+
raise RenderExit
|
|
141
|
+
end
|
|
142
|
+
|
|
143
|
+
# Redirect to the referer of this method.
|
|
144
|
+
|
|
145
|
+
def redirect_referer(postfix = nil, status = 303)
|
|
146
|
+
redirect("#{@context.referer}#{postfix}", status)
|
|
147
|
+
end
|
|
148
|
+
|
|
149
|
+
# Log a rendering error.
|
|
150
|
+
|
|
151
|
+
def log_error(error, path)
|
|
152
|
+
@rendering_errors ||= []
|
|
153
|
+
@rendering_errors << [error, path]
|
|
154
|
+
|
|
155
|
+
# gmosx: Hmm perhaps this should not be logged
|
|
156
|
+
# to avoid DOS attacks.
|
|
157
|
+
|
|
158
|
+
Logger.error "Error while handling '#{path}'."
|
|
159
|
+
Logger.error pp_exception(error)
|
|
160
|
+
end
|
|
161
|
+
|
|
162
|
+
# Convenience method to lookup the session.
|
|
163
|
+
|
|
164
|
+
def session
|
|
165
|
+
@context.session
|
|
166
|
+
end
|
|
167
|
+
|
|
168
|
+
# Add some text to the output buffer.
|
|
169
|
+
|
|
170
|
+
def render_text(text)
|
|
171
|
+
@out << text
|
|
172
|
+
end
|
|
173
|
+
alias_method :print, :render_text
|
|
174
|
+
|
|
175
|
+
# Access the programmatic renderer (builder).
|
|
176
|
+
|
|
177
|
+
def build(&block)
|
|
178
|
+
if block.arity == 1
|
|
179
|
+
yield XmlBuilder.new(@out)
|
|
180
|
+
else
|
|
181
|
+
XmlBuilder.new(@out).instance_eval(&block)
|
|
182
|
+
end
|
|
183
|
+
end
|
|
184
|
+
|
|
185
|
+
# Return a programmatic renderer that targets the
|
|
186
|
+
# output buffer.
|
|
187
|
+
|
|
188
|
+
def builder
|
|
189
|
+
XmlBuilder.new(@out)
|
|
190
|
+
end
|
|
191
|
+
|
|
192
|
+
# A Helper class to access rendering mixins. Useful to avoid
|
|
193
|
+
# poluting the Render with utility methods.
|
|
194
|
+
#--
|
|
195
|
+
# TODO: find a less confusing name.
|
|
196
|
+
#++
|
|
197
|
+
|
|
198
|
+
class Emitter
|
|
199
|
+
include Singleton
|
|
200
|
+
include XhtmlMixin
|
|
201
|
+
include FormMixin
|
|
202
|
+
include TableMixin
|
|
203
|
+
end
|
|
204
|
+
|
|
205
|
+
# A helper to access the utilities emitter:
|
|
206
|
+
#
|
|
207
|
+
# #{emit :form, entity}
|
|
208
|
+
# #{emit :options, :labels => [..], :values => [..], :selected => 1}
|
|
209
|
+
#
|
|
210
|
+
# Useful to avoid poluting the render with mixin methods.
|
|
211
|
+
|
|
212
|
+
def emit(meth, *options)
|
|
213
|
+
Emitter.instance.send(meth, *options)
|
|
214
|
+
end
|
|
391
215
|
|
|
392
216
|
end
|
|
393
217
|
|