nitro 0.25.0 → 0.26.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 +531 -1
- data/ProjectInfo +29 -5
- data/README +1 -1
- data/doc/AUTHORS +12 -6
- data/doc/RELEASES +114 -0
- data/lib/glue/sweeper.rb +71 -0
- data/lib/nitro.rb +19 -12
- data/lib/nitro/adapter/cgi.rb +4 -0
- data/lib/nitro/adapter/webrick.rb +4 -2
- data/lib/nitro/caching.rb +1 -0
- data/lib/nitro/caching/fragments.rb +7 -1
- data/lib/nitro/caching/output.rb +6 -1
- data/lib/nitro/caching/stores.rb +13 -1
- data/lib/nitro/cgi.rb +9 -1
- data/lib/nitro/cgi/request.rb +11 -3
- data/lib/nitro/cgi/utils.rb +24 -2
- data/lib/nitro/compiler.rb +89 -63
- data/lib/nitro/compiler/cleanup.rb +16 -0
- data/lib/nitro/compiler/elements.rb +117 -0
- data/lib/nitro/compiler/markup.rb +3 -1
- data/lib/nitro/compiler/morphing.rb +203 -73
- data/lib/nitro/compiler/script_generator.rb +14 -0
- data/lib/nitro/compiler/shaders.rb +1 -1
- data/lib/nitro/context.rb +5 -6
- data/lib/nitro/controller.rb +43 -21
- data/lib/nitro/dispatcher.rb +86 -37
- data/lib/nitro/element.rb +3 -105
- data/lib/nitro/helper/benchmark.rb +3 -0
- data/lib/nitro/helper/dojo.rb +0 -0
- data/lib/nitro/helper/form.rb +85 -255
- data/lib/nitro/helper/form/controls.rb +274 -0
- data/lib/nitro/helper/javascript.rb +86 -6
- data/lib/nitro/helper/pager.rb +5 -0
- data/lib/nitro/helper/prototype.rb +49 -0
- data/lib/nitro/helper/scriptaculous.rb +0 -0
- data/lib/nitro/helper/xhtml.rb +11 -8
- data/lib/nitro/helper/xml.rb +1 -1
- data/lib/nitro/routing.rb +8 -1
- data/lib/nitro/scaffolding.rb +344 -0
- data/lib/nitro/server.rb +5 -1
- data/lib/nitro/server/runner.rb +19 -15
- data/lib/nitro/session.rb +32 -56
- data/lib/nitro/session/drbserver.rb +1 -1
- data/lib/nitro/session/file.rb +34 -15
- data/lib/nitro/session/memory.rb +13 -4
- data/lib/nitro/session/og.rb +56 -0
- data/proto/public/js/controls.js +30 -1
- data/proto/public/js/dragdrop.js +211 -146
- data/proto/public/js/effects.js +261 -399
- data/proto/public/js/prototype.js +131 -72
- data/proto/public/scaffold/edit.xhtml +10 -3
- data/proto/public/scaffold/form.xhtml +1 -7
- data/proto/public/scaffold/index.xhtml +20 -0
- data/proto/public/scaffold/list.xhtml +15 -8
- data/proto/public/scaffold/new.xhtml +10 -3
- data/proto/public/scaffold/search.xhtml +28 -0
- data/proto/public/scaffold/view.xhtml +8 -0
- data/proto/run.rb +93 -1
- data/src/part/admin.rb +4 -2
- data/src/part/admin/controller.rb +62 -28
- data/src/part/admin/skin.rb +8 -8
- data/src/part/admin/system.css +135 -0
- data/src/part/admin/template/index.xhtml +8 -12
- data/test/nitro/caching/tc_stores.rb +17 -0
- data/test/nitro/tc_caching.rb +1 -4
- data/test/nitro/tc_dispatcher.rb +22 -10
- data/test/nitro/tc_element.rb +1 -1
- data/test/nitro/tc_session.rb +23 -11
- data/test/public/blog/another/very_litle/index.xhtml +1 -0
- metadata +29 -15
- data/lib/nitro/dispatcher/general.rb +0 -62
- data/lib/nitro/dispatcher/nice.rb +0 -57
- data/lib/nitro/scaffold.rb +0 -171
- data/proto/public/index.xhtml +0 -83
- data/proto/public/js/scaffold.js +0 -74
- data/proto/public/settings.xhtml +0 -66
data/lib/nitro/caching/stores.rb
CHANGED
|
@@ -2,6 +2,8 @@ require 'fileutils'
|
|
|
2
2
|
|
|
3
3
|
require 'mega/synchash'
|
|
4
4
|
|
|
5
|
+
require 'glue/attribute'
|
|
6
|
+
|
|
5
7
|
module Nitro
|
|
6
8
|
|
|
7
9
|
# Adds support for caching.
|
|
@@ -20,8 +22,10 @@ module Caching
|
|
|
20
22
|
self[name] = content
|
|
21
23
|
end
|
|
22
24
|
|
|
25
|
+
alias_method :old_delete, :delete
|
|
26
|
+
|
|
23
27
|
def delete(name, options = {})
|
|
24
|
-
|
|
28
|
+
old_delete(name)
|
|
25
29
|
end
|
|
26
30
|
|
|
27
31
|
end
|
|
@@ -69,8 +73,16 @@ module Caching
|
|
|
69
73
|
|
|
70
74
|
end
|
|
71
75
|
|
|
76
|
+
#--
|
|
77
|
+
# TODO: implement me
|
|
78
|
+
#++
|
|
79
|
+
|
|
72
80
|
class DrbStrore
|
|
73
81
|
end
|
|
82
|
+
|
|
83
|
+
#--
|
|
84
|
+
# TODO: implement me
|
|
85
|
+
#++
|
|
74
86
|
|
|
75
87
|
class MemcacheStore
|
|
76
88
|
end
|
data/lib/nitro/cgi.rb
CHANGED
|
@@ -1,3 +1,5 @@
|
|
|
1
|
+
require 'cgi'
|
|
2
|
+
|
|
1
3
|
require 'glue/configuration'
|
|
2
4
|
require 'nitro/cgi/http'
|
|
3
5
|
|
|
@@ -23,6 +25,12 @@ class Cgi
|
|
|
23
25
|
context.in = inp
|
|
24
26
|
context.headers = cgi.env
|
|
25
27
|
|
|
28
|
+
# gmosx: QUERY_STRING is sometimes not populated.
|
|
29
|
+
|
|
30
|
+
if context.query_string.empty? and context.uri =~ /\?/
|
|
31
|
+
context.headers['QUERY_STRING'] = context.uri.split('?').last
|
|
32
|
+
end
|
|
33
|
+
|
|
26
34
|
Cgi.parse_params(context)
|
|
27
35
|
Cgi.parse_cookies(context)
|
|
28
36
|
context.render(context.path)
|
|
@@ -293,4 +301,4 @@ end
|
|
|
293
301
|
end
|
|
294
302
|
|
|
295
303
|
# * George Moschovitis <gm@navel.gr>
|
|
296
|
-
# * Guillaume Pierronnet <guillaume.pierronnet@gmail.com>
|
|
304
|
+
# * Guillaume Pierronnet <guillaume.pierronnet@gmail.com>
|
data/lib/nitro/cgi/request.rb
CHANGED
|
@@ -39,7 +39,7 @@ module Request
|
|
|
39
39
|
# 443 == port
|
|
40
40
|
@headers['HTTPS'] == 'on'
|
|
41
41
|
end
|
|
42
|
-
|
|
42
|
+
|
|
43
43
|
# The request uri.
|
|
44
44
|
|
|
45
45
|
def uri
|
|
@@ -84,7 +84,14 @@ module Request
|
|
|
84
84
|
#++
|
|
85
85
|
|
|
86
86
|
def query_string
|
|
87
|
-
|
|
87
|
+
=begin
|
|
88
|
+
qs = headers['QUERY_STRING']
|
|
89
|
+
if qs.empty? and uri =~ /\?/
|
|
90
|
+
qs = headers['QUERY_STRING'] = uri.split('?').last
|
|
91
|
+
end
|
|
92
|
+
return qs
|
|
93
|
+
=end
|
|
94
|
+
headers['QUERY_STRING']
|
|
88
95
|
end
|
|
89
96
|
|
|
90
97
|
# The request method. Alternatively you could use the
|
|
@@ -160,6 +167,7 @@ module Request
|
|
|
160
167
|
not /XMLHttpRequest/i.match(@headers['HTTP_X_REQUESTED_WITH']).nil?
|
|
161
168
|
end
|
|
162
169
|
alias xhr? :xml_http_request?
|
|
170
|
+
alias ajax? :xml_http_request?
|
|
163
171
|
|
|
164
172
|
# Return the referer. For the initial page in the
|
|
165
173
|
# clickstream there is no referer, set "/" by default.
|
|
@@ -187,7 +195,7 @@ module Request
|
|
|
187
195
|
|
|
188
196
|
if @headers.include?('HTTP_X_FORWARDED_FOR') then
|
|
189
197
|
remote_ips = @headers['HTTP_X_FORWARDED_FOR'].split(',').reject do |ip|
|
|
190
|
-
ip =~ /^unknown$|^(10|172\.16|192\.168)\./i
|
|
198
|
+
ip =~ /^unknown$|^(127|10|172\.16|192\.168)\./i
|
|
191
199
|
end
|
|
192
200
|
|
|
193
201
|
return remote_ips.first.strip unless remote_ips.empty?
|
data/lib/nitro/cgi/utils.rb
CHANGED
|
@@ -10,7 +10,9 @@ module Request
|
|
|
10
10
|
# Different servers hold user agent in differnet
|
|
11
11
|
# strings (unify this).
|
|
12
12
|
|
|
13
|
-
def user_agent
|
|
14
13
|
headers['HTTP_USER_AGENT'] || headers['USER-AGENT']
|
|
15
14
|
end
|
|
15
|
+
def user_agent
|
|
16
|
+
headers['HTTP_USER_AGENT'] || headers['USER-AGENT']
|
|
17
|
+
end
|
|
16
18
|
|
|
17
19
|
def from_gecko?
|
|
18
20
|
user_agent =~ /Gecko/
|
|
@@ -34,9 +36,29 @@ module Request
|
|
|
34
36
|
def from_w3c?
|
|
35
37
|
from_gecko? or from_khtml? or from_opera?
|
|
36
38
|
end
|
|
39
|
+
|
|
40
|
+
def from_osx?
|
|
41
|
+
user_agent =~ /Mac OS X/ or user_agent =~ /Mac_PowerPC/
|
|
42
|
+
end
|
|
43
|
+
|
|
44
|
+
def from_os9?
|
|
45
|
+
end
|
|
46
|
+
|
|
47
|
+
def from_mac?
|
|
48
|
+
from_osx? or from_os9?
|
|
49
|
+
end
|
|
50
|
+
|
|
51
|
+
def from_unix?
|
|
52
|
+
user_agent =~ /linux/ or user_agent =~ /FreeBSD/
|
|
53
|
+
end
|
|
54
|
+
alias_method :from_linux?, :from_unix?
|
|
55
|
+
|
|
56
|
+
def from_windows?
|
|
57
|
+
user_agent =~ /Windows/
|
|
58
|
+
end
|
|
37
59
|
|
|
38
60
|
end
|
|
39
61
|
|
|
40
62
|
end
|
|
41
63
|
|
|
42
|
-
# * Chris Farmiloe <chris.farmiloe@farmiloe.com>
|
|
64
|
+
# * Chris Farmiloe <chris.farmiloe@farmiloe.com>
|
data/lib/nitro/compiler.rb
CHANGED
|
@@ -1,7 +1,13 @@
|
|
|
1
1
|
require 'nano/kernel/singleton'
|
|
2
2
|
|
|
3
3
|
require 'glue/template'
|
|
4
|
+
|
|
5
|
+
require 'nitro/compiler/elements'
|
|
4
6
|
require 'nitro/compiler/errors'
|
|
7
|
+
require 'nitro/compiler/markup'
|
|
8
|
+
require 'nitro/compiler/morphing'
|
|
9
|
+
require 'nitro/compiler/include'
|
|
10
|
+
require 'nitro/compiler/cleanup'
|
|
5
11
|
|
|
6
12
|
module Nitro
|
|
7
13
|
|
|
@@ -13,6 +19,10 @@ class Compiler
|
|
|
13
19
|
unless const_defined? :PROTO_TEMPLATE_ROOT
|
|
14
20
|
PROTO_TEMPLATE_ROOT = "#{Nitro.proto_path}/public"
|
|
15
21
|
end
|
|
22
|
+
|
|
23
|
+
# The controller for this compiler.
|
|
24
|
+
|
|
25
|
+
attr_accessor :controller
|
|
16
26
|
|
|
17
27
|
# Set to true to force reloading of code and templates for
|
|
18
28
|
# each request. Extremely useful during development. Must be
|
|
@@ -20,50 +30,81 @@ class Compiler
|
|
|
20
30
|
# performance penalty.
|
|
21
31
|
|
|
22
32
|
setting :reload, :default => true, :doc => 'If true all code and templates are reloaded in each request'
|
|
23
|
-
|
|
33
|
+
|
|
34
|
+
def initialize(controller = nil)
|
|
35
|
+
@controller = controller
|
|
36
|
+
end
|
|
37
|
+
|
|
24
38
|
# Action names with double underscores (__) are converted
|
|
25
39
|
# to subdirectories. Here are some example mappings:
|
|
26
40
|
#
|
|
27
41
|
# hello_world -> template_root/hello_world.xhtml
|
|
28
42
|
# this__is__my__hello_world -> template_root/this/is/my/hello_world
|
|
29
43
|
|
|
30
|
-
def template_for_action(action,
|
|
31
|
-
|
|
32
|
-
|
|
44
|
+
def template_for_action(action, ext = Template.extension)
|
|
45
|
+
cklass = @controller
|
|
46
|
+
template_root = nil
|
|
47
|
+
checked_proto = nil
|
|
48
|
+
|
|
49
|
+
# search for template in controller and its ancestors's template_root
|
|
50
|
+
# if there is not an action defined in controller then also check in
|
|
51
|
+
# PROTO_TEMPLATE_ROOT
|
|
33
52
|
|
|
34
|
-
|
|
53
|
+
loop do
|
|
54
|
+
if cklass.respond_to?(:template_root) && template_root = cklass.template_root
|
|
55
|
+
cklass = cklass.superclass
|
|
56
|
+
elsif !@controller.respond_to?(:action) && !checked_proto
|
|
57
|
+
template_root = PROTO_TEMPLATE_ROOT
|
|
58
|
+
checked_proto = true
|
|
59
|
+
else
|
|
60
|
+
return nil # no template found
|
|
61
|
+
end
|
|
62
|
+
|
|
63
|
+
# attempt to find a template of the form
|
|
64
|
+
# template_root/action.xhtml
|
|
35
65
|
|
|
36
|
-
|
|
66
|
+
path = "#{template_root}/#{action.gsub(/__/, '/')}.#{ext}".squeeze('/')
|
|
67
|
+
return path if File.exist?(path)
|
|
68
|
+
|
|
37
69
|
# attempt to find a template of the form
|
|
38
70
|
# template_root/action/index.xhtml
|
|
39
71
|
|
|
40
72
|
path = "#{template_root}/#{action.gsub(/__/, '/')}/#{Template.default}.#{ext}".squeeze('/')
|
|
41
|
-
|
|
42
|
-
unless File.exist?(path)
|
|
43
|
-
# No template found!
|
|
44
|
-
return nil
|
|
45
|
-
end
|
|
73
|
+
return path if File.exist?(path)
|
|
46
74
|
end
|
|
75
|
+
end
|
|
76
|
+
alias_method :template?, :template_for_action
|
|
77
|
+
|
|
78
|
+
# Helper.
|
|
47
79
|
|
|
48
|
-
|
|
80
|
+
def action?(sym)
|
|
81
|
+
return @controller.action_methods.include?(sym.to_s)
|
|
49
82
|
end
|
|
50
83
|
|
|
51
|
-
# This
|
|
84
|
+
# This method transforms the template. Typically
|
|
52
85
|
# template processors are added as aspects to this method
|
|
53
86
|
# to allow for customized template transformation prior
|
|
54
87
|
# to compilation.
|
|
55
88
|
#
|
|
56
89
|
# The default transformation extracts the Ruby code from
|
|
57
|
-
# processing instructions
|
|
90
|
+
# processing instructions, and uses the StaticInclude,
|
|
91
|
+
# Morphing, Elements and Markup compiler modules.
|
|
58
92
|
|
|
59
93
|
def transform_template(template)
|
|
60
|
-
|
|
94
|
+
template = StaticInclude.transform(template)
|
|
95
|
+
template = Morphing.transform(template)
|
|
96
|
+
template = Elements.transform(template)
|
|
97
|
+
template = Markup.transform(template)
|
|
98
|
+
template = Cleanup.transform(template)
|
|
99
|
+
template = Template.transform(template)
|
|
61
100
|
end
|
|
62
101
|
|
|
63
102
|
# Compile the template into a render method.
|
|
103
|
+
# Don't compile the template if the controller
|
|
104
|
+
# responds_to? #{action}_template.
|
|
64
105
|
|
|
65
|
-
def compile_template(
|
|
66
|
-
Logger.debug "Compiling template '#{
|
|
106
|
+
def compile_template(action, path)
|
|
107
|
+
Logger.debug "Compiling template '#{@controller}: #{path}'" if $DBG
|
|
67
108
|
|
|
68
109
|
template = File.read(path)
|
|
69
110
|
|
|
@@ -74,7 +115,7 @@ class Compiler
|
|
|
74
115
|
}
|
|
75
116
|
|
|
76
117
|
begin
|
|
77
|
-
|
|
118
|
+
@controller.class_eval(code, path)
|
|
78
119
|
rescue SyntaxError => e
|
|
79
120
|
raise TemplateCompileError.new(code, template, e)
|
|
80
121
|
end
|
|
@@ -88,18 +129,12 @@ class Compiler
|
|
|
88
129
|
# TODO: cleanup this method.
|
|
89
130
|
#++
|
|
90
131
|
|
|
91
|
-
def compile_action(
|
|
92
|
-
#--
|
|
93
|
-
# gmosx: Move elsewhere.
|
|
94
|
-
#++
|
|
95
|
-
|
|
96
|
-
Aspects.include_advice_modules(klass)
|
|
97
|
-
|
|
132
|
+
def compile_action(action)
|
|
98
133
|
action = action.to_s.gsub(/_action$/, '')
|
|
99
|
-
|
|
134
|
+
|
|
100
135
|
return false unless action
|
|
101
136
|
|
|
102
|
-
Logger.debug "Compiling action '
|
|
137
|
+
Logger.debug "Compiling action '#@controller##{action}'" if $DBG
|
|
103
138
|
|
|
104
139
|
valid = false
|
|
105
140
|
|
|
@@ -111,16 +146,16 @@ class Compiler
|
|
|
111
146
|
|
|
112
147
|
# Inject the pre advices.
|
|
113
148
|
|
|
114
|
-
code << Aspects.gen_advice_code(action,
|
|
149
|
+
code << Aspects.gen_advice_code(action, @controller.advices, :pre)
|
|
115
150
|
|
|
116
151
|
# Call the action
|
|
117
152
|
|
|
118
|
-
if
|
|
153
|
+
if @controller.action_methods.include?(action)
|
|
119
154
|
valid = true
|
|
120
155
|
|
|
121
156
|
# Annotated parameters.
|
|
122
157
|
|
|
123
|
-
if params =
|
|
158
|
+
if params = @controller.ann(action.to_sym).params and (!params.nil?)
|
|
124
159
|
params = params.collect { |p| "@#{p} = @context['#{p}']" }
|
|
125
160
|
code << "#{params.join(';')}"
|
|
126
161
|
end
|
|
@@ -128,7 +163,7 @@ class Compiler
|
|
|
128
163
|
# Try to resolve action parameters. Returns negative
|
|
129
164
|
# numbers for arbitrary parameters.
|
|
130
165
|
|
|
131
|
-
param_count =
|
|
166
|
+
param_count = @controller.instance_method(action.intern).arity
|
|
132
167
|
|
|
133
168
|
if param_count != 0
|
|
134
169
|
if param_count > 0
|
|
@@ -172,38 +207,22 @@ class Compiler
|
|
|
172
207
|
|
|
173
208
|
# Take :view annotation into account.
|
|
174
209
|
|
|
175
|
-
view =
|
|
210
|
+
view = @controller.ann(action.to_sym).view # FIXME
|
|
176
211
|
view = action if view.nil?
|
|
177
212
|
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
end
|
|
187
|
-
|
|
188
|
-
# Don't use a proto template if there is an action
|
|
189
|
-
# defined.
|
|
190
|
-
|
|
191
|
-
template_root ||= PROTO_TEMPLATE_ROOT unless valid
|
|
192
|
-
|
|
193
|
-
if template_root and template_path = template_for_action(view.to_s, template_root)
|
|
194
|
-
valid = true
|
|
195
|
-
code << %{
|
|
196
|
-
#{action}_template;
|
|
197
|
-
}
|
|
198
|
-
break
|
|
199
|
-
end
|
|
200
|
-
|
|
201
|
-
break unless cklass = cklass.superclass
|
|
213
|
+
# Search the [controller] class and it's ancestors for the template
|
|
214
|
+
|
|
215
|
+
template_path = template_for_action(view.to_s)
|
|
216
|
+
if template_path or @controller.instance_methods.include?("#{action}_template")
|
|
217
|
+
valid = true
|
|
218
|
+
code << %{
|
|
219
|
+
#{action}_template
|
|
220
|
+
}
|
|
202
221
|
end
|
|
203
222
|
|
|
204
223
|
return false unless valid
|
|
205
224
|
|
|
206
|
-
if
|
|
225
|
+
if @controller.action_methods.include?(action)
|
|
207
226
|
code << %{
|
|
208
227
|
if @out.empty? and action_return_value.is_a?(String)
|
|
209
228
|
print(action_return_value)
|
|
@@ -220,7 +239,7 @@ class Compiler
|
|
|
220
239
|
|
|
221
240
|
# Inject the post advices.
|
|
222
241
|
|
|
223
|
-
code << Aspects.gen_advice_code(action,
|
|
242
|
+
code << Aspects.gen_advice_code(action, @controller.advices, :post)
|
|
224
243
|
|
|
225
244
|
code << %{
|
|
226
245
|
@action_name = @parent_action_name
|
|
@@ -230,12 +249,16 @@ class Compiler
|
|
|
230
249
|
# First compile the action method.
|
|
231
250
|
|
|
232
251
|
# begin
|
|
233
|
-
|
|
252
|
+
@controller.class_eval(code)
|
|
234
253
|
# rescue SyntaxError => e
|
|
235
254
|
# raise ActionCompileError.new(code, action, e)
|
|
236
255
|
# end
|
|
237
|
-
|
|
238
|
-
|
|
256
|
+
|
|
257
|
+
unless @controller.respond_to?("#{action}_template")
|
|
258
|
+
if template_path
|
|
259
|
+
compile_template(action, template_path)
|
|
260
|
+
end
|
|
261
|
+
end
|
|
239
262
|
|
|
240
263
|
return true
|
|
241
264
|
end
|
|
@@ -243,8 +266,8 @@ class Compiler
|
|
|
243
266
|
# Compiles an action method in the given (controller) class.
|
|
244
267
|
# A sync is used to make compilation thread safe.
|
|
245
268
|
|
|
246
|
-
def compile(
|
|
247
|
-
compile_action(
|
|
269
|
+
def compile(action)
|
|
270
|
+
compile_action(action)
|
|
248
271
|
end
|
|
249
272
|
|
|
250
273
|
# :section: Helper methods.
|
|
@@ -279,3 +302,6 @@ class Compiler
|
|
|
279
302
|
end
|
|
280
303
|
|
|
281
304
|
end
|
|
305
|
+
|
|
306
|
+
# * George Moschovitis <gm@navel.gr>
|
|
307
|
+
# * Chris Farmiloe <chris.farmiloe@farmiloe.com>
|
|
@@ -0,0 +1,117 @@
|
|
|
1
|
+
require 'rexml/document'
|
|
2
|
+
require 'rexml/streamlistener'
|
|
3
|
+
|
|
4
|
+
require 'nitro/element'
|
|
5
|
+
require "glue/html"
|
|
6
|
+
|
|
7
|
+
module Nitro
|
|
8
|
+
|
|
9
|
+
# A compiler that handles the processing of Elements
|
|
10
|
+
|
|
11
|
+
class Elements # :nodoc: all
|
|
12
|
+
|
|
13
|
+
class Listener # :nodoc: all
|
|
14
|
+
include REXML::StreamListener
|
|
15
|
+
|
|
16
|
+
attr_accessor :buffer
|
|
17
|
+
attr_accessor :stack
|
|
18
|
+
|
|
19
|
+
def initialize
|
|
20
|
+
super
|
|
21
|
+
@buffer = ''
|
|
22
|
+
@stack = []
|
|
23
|
+
end
|
|
24
|
+
|
|
25
|
+
PREFIX_RE = /^#{Element.prefix}:/
|
|
26
|
+
CAPITALIZED_RE = /^[A-Z]/
|
|
27
|
+
|
|
28
|
+
def tag_start(name, attributes)
|
|
29
|
+
# check if the name starts with the element prefix, or
|
|
30
|
+
# is capitalized.
|
|
31
|
+
if name =~ PREFIX_RE or name =~ CAPITALIZED_RE
|
|
32
|
+
name = name.split(':')[1].camelize if name =~ PREFIX_RE
|
|
33
|
+
|
|
34
|
+
obj = Object.const_get(name).new
|
|
35
|
+
|
|
36
|
+
attributes.each do | k, v |
|
|
37
|
+
obj.instance_variable_set("@#{k}", v)
|
|
38
|
+
end
|
|
39
|
+
|
|
40
|
+
@stack.push [obj, @buffer, @parent]
|
|
41
|
+
|
|
42
|
+
@buffer = obj._text
|
|
43
|
+
@parent.add_child(obj) if @parent
|
|
44
|
+
|
|
45
|
+
@parent = obj
|
|
46
|
+
else # This is a static element.
|
|
47
|
+
attrs = []
|
|
48
|
+
|
|
49
|
+
attributes.each do | k, v |
|
|
50
|
+
attrs << %|#{k}="#{v}"|
|
|
51
|
+
end
|
|
52
|
+
|
|
53
|
+
attrs = attrs.empty? ? nil : " #{attrs.join(' ')}"
|
|
54
|
+
|
|
55
|
+
@buffer << "<#{name}#{attrs}>"
|
|
56
|
+
end
|
|
57
|
+
end
|
|
58
|
+
|
|
59
|
+
def tag_end(name)
|
|
60
|
+
# check if the name starts with the element prefix, or
|
|
61
|
+
# is capitalized.
|
|
62
|
+
if name =~ PREFIX_RE or name =~ CAPITALIZED_RE
|
|
63
|
+
name = name.split(':')[1].camelize if name =~ PREFIX_RE
|
|
64
|
+
obj, @buffer, @parent = @stack.pop
|
|
65
|
+
@buffer << obj.render
|
|
66
|
+
else
|
|
67
|
+
@buffer << "</#{name}>"
|
|
68
|
+
end
|
|
69
|
+
end
|
|
70
|
+
|
|
71
|
+
def text(str)
|
|
72
|
+
@buffer << str
|
|
73
|
+
end
|
|
74
|
+
|
|
75
|
+
def instruction(name, attributes)
|
|
76
|
+
@buffer << "<?#{name}#{attributes}?>"
|
|
77
|
+
end
|
|
78
|
+
|
|
79
|
+
def comment(c)
|
|
80
|
+
unless Template.strip_xml_comments
|
|
81
|
+
@buffer << "<!--#{c}-->"
|
|
82
|
+
end
|
|
83
|
+
end
|
|
84
|
+
end
|
|
85
|
+
|
|
86
|
+
class << self
|
|
87
|
+
def parse(source)
|
|
88
|
+
self.new.parse(source)
|
|
89
|
+
end
|
|
90
|
+
|
|
91
|
+
def transform(source)
|
|
92
|
+
self.new.transform(source)
|
|
93
|
+
end
|
|
94
|
+
end
|
|
95
|
+
|
|
96
|
+
# Expand the elemens found in source.
|
|
97
|
+
|
|
98
|
+
def transform(source)
|
|
99
|
+
listener = Listener.new
|
|
100
|
+
REXML::Document.parse_stream(source, listener)
|
|
101
|
+
# gmosx, FIXME: optimize this, how?
|
|
102
|
+
# gmosx, FIXME: this is a hack fix, improve.
|
|
103
|
+
# TODO:farms why is cleanup called this many times?!?!? ... waste of gsubs
|
|
104
|
+
return listener.buffer
|
|
105
|
+
end
|
|
106
|
+
end
|
|
107
|
+
|
|
108
|
+
# An (old) alias.
|
|
109
|
+
|
|
110
|
+
unless const_defined? :ElementProcessor
|
|
111
|
+
ElementProcessor = Elements
|
|
112
|
+
end
|
|
113
|
+
|
|
114
|
+
end
|
|
115
|
+
|
|
116
|
+
# * George Moschovitis <gm@navel.gr>
|
|
117
|
+
# * Chris Farmiloe <chris.farmiloe@farmiloe.com>
|