nitro 0.29.0 → 0.30.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (93) hide show
  1. data/CHANGELOG +410 -0
  2. data/ProjectInfo +36 -44
  3. data/README +5 -5
  4. data/doc/AUTHORS +6 -0
  5. data/doc/RELEASES +159 -2
  6. data/lib/glue/sweeper.rb +2 -2
  7. data/lib/glue/webfile.rb +14 -1
  8. data/lib/nitro.rb +6 -9
  9. data/lib/nitro/adapter/mongrel.rb +36 -43
  10. data/lib/nitro/adapter/scgi.rb +1 -1
  11. data/lib/nitro/adapter/webrick.rb +96 -24
  12. data/lib/nitro/caching/actions.rb +2 -1
  13. data/lib/nitro/caching/fragments.rb +1 -8
  14. data/lib/nitro/caching/output.rb +14 -4
  15. data/lib/nitro/cgi.rb +19 -21
  16. data/lib/nitro/cgi/cookie.rb +5 -1
  17. data/lib/nitro/cgi/request.rb +20 -4
  18. data/lib/nitro/compiler.rb +74 -28
  19. data/lib/nitro/compiler/cleanup.rb +1 -1
  20. data/lib/nitro/compiler/elements.rb +1 -2
  21. data/lib/nitro/compiler/localization.rb +1 -1
  22. data/lib/nitro/compiler/markup.rb +1 -1
  23. data/lib/nitro/compiler/script.rb +52 -44
  24. data/lib/nitro/compiler/squeeze.rb +4 -3
  25. data/lib/nitro/compiler/xslt.rb +7 -6
  26. data/lib/nitro/context.rb +39 -20
  27. data/lib/nitro/controller.rb +24 -5
  28. data/lib/nitro/dispatcher.rb +13 -5
  29. data/lib/nitro/global.rb +63 -0
  30. data/lib/nitro/helper/feed.rb +432 -0
  31. data/lib/nitro/helper/form.rb +11 -3
  32. data/lib/nitro/helper/form/builder.rb +140 -0
  33. data/lib/nitro/helper/form/controls.rb +2 -1
  34. data/lib/nitro/helper/javascript.rb +6 -0
  35. data/lib/nitro/helper/javascript/morphing.rb +13 -6
  36. data/lib/nitro/helper/xhtml.rb +42 -6
  37. data/lib/nitro/helper/xml.rb +3 -0
  38. data/lib/nitro/part.rb +2 -2
  39. data/lib/nitro/render.rb +7 -2
  40. data/lib/nitro/router.rb +57 -16
  41. data/lib/nitro/scaffolding.rb +29 -20
  42. data/lib/nitro/server.rb +4 -10
  43. data/lib/nitro/server/drb.rb +1 -1
  44. data/lib/nitro/server/runner.rb +10 -0
  45. data/lib/nitro/session.rb +31 -12
  46. data/lib/nitro/session/drb.rb +13 -1
  47. data/lib/nitro/session/file.rb +1 -1
  48. data/lib/nitro/session/memcached.rb +1 -1
  49. data/lib/nitro/session/memory.rb +1 -1
  50. data/lib/nitro/session/og.rb +1 -1
  51. data/lib/nitro/test/testcase.rb +3 -0
  52. data/proto/public/error.xhtml +5 -5
  53. data/proto/public/js/controls.js +2 -2
  54. data/proto/public/js/dragdrop.js +320 -79
  55. data/proto/public/js/effects.js +200 -152
  56. data/proto/public/js/prototype.js +284 -63
  57. data/proto/public/js/scriptaculous.js +7 -5
  58. data/proto/public/js/unittest.js +11 -0
  59. data/proto/public/scaffold/advanced_search.xhtml +30 -0
  60. data/proto/public/scaffold/list.xhtml +8 -1
  61. data/proto/public/scaffold/search.xhtml +2 -1
  62. data/proto/script/scgi_service +1 -1
  63. data/src/part/admin/controller.rb +1 -1
  64. data/src/part/admin/skin.rb +1 -1
  65. data/test/nitro/CONFIG.rb +3 -0
  66. data/test/nitro/adapter/tc_webrick.rb +1 -1
  67. data/test/nitro/cgi/tc_cookie.rb +1 -1
  68. data/test/nitro/cgi/tc_request.rb +5 -5
  69. data/test/nitro/compiler/tc_client_morpher.rb +47 -0
  70. data/test/nitro/compiler/tc_compiler.rb +2 -0
  71. data/test/nitro/helper/tc_feed.rb +138 -0
  72. data/test/nitro/helper/tc_pager.rb +1 -1
  73. data/test/nitro/helper/tc_rss.rb +1 -1
  74. data/test/nitro/helper/tc_table.rb +1 -1
  75. data/test/nitro/helper/tc_xhtml.rb +1 -1
  76. data/test/nitro/tc_caching.rb +1 -1
  77. data/test/nitro/tc_cgi.rb +1 -1
  78. data/test/nitro/tc_context.rb +1 -1
  79. data/test/nitro/tc_controller.rb +31 -3
  80. data/test/nitro/tc_controller_aspect.rb +1 -1
  81. data/test/nitro/tc_dispatcher.rb +1 -1
  82. data/test/nitro/tc_element.rb +1 -1
  83. data/test/nitro/tc_flash.rb +1 -1
  84. data/test/nitro/tc_helper.rb +1 -1
  85. data/test/nitro/tc_render.rb +6 -6
  86. data/test/nitro/tc_router.rb +8 -4
  87. data/test/nitro/tc_server.rb +1 -3
  88. data/test/nitro/tc_session.rb +1 -3
  89. metadata +107 -104
  90. data/Rakefile +0 -232
  91. data/lib/nitro/adapter/acgi.rb +0 -237
  92. data/proto/public/Makefile.acgi +0 -40
  93. data/proto/public/acgi.c +0 -138
@@ -21,6 +21,8 @@ module Caching
21
21
 
22
22
  module ClassMethods
23
23
 
24
+ # Cache the given actions.
25
+
24
26
  def cache_action(*actions)
25
27
  return unless caching_enabled?
26
28
 
@@ -49,7 +51,6 @@ module Caching
49
51
  private
50
52
 
51
53
  # Expire the cached fragment of the given actions.
52
- #
53
54
  #--
54
55
  # FIXME: not very good implementation?
55
56
  #++
@@ -17,14 +17,7 @@ module Caching
17
17
 
18
18
  # The cache used to store the fragments.
19
19
 
20
- setting :cache, :default => nil, :doc => 'The cache used to store the fragments'
21
-
22
- #--
23
- # gmosx, FIXME: this is a hack, improve setting
24
- # implementation.
25
- #++
26
-
27
- @@cache = Glue::MemoryCache.new
20
+ setting :cache, :default => Glue::MemoryCache.new, :doc => 'The cache used to store the fragments'
28
21
 
29
22
  def self.get(name, options = {})
30
23
  return @@cache.get(name, options)
@@ -11,7 +11,8 @@ module Caching
11
11
  # (Lighttpd, Apache, etc) for optimal performance.
12
12
  #
13
13
  # Nitro promotes coding your application in such a way as to
14
- # allow for output caching to the greatest extend.
14
+ # allow for output caching to the greatest extend. Output
15
+ # caching *is your friend*.
15
16
  #--
16
17
  # gmosx, FIXME: Don't create excessive directories, use better
17
18
  # rewrite rules to handle xxx.html files.
@@ -66,6 +67,9 @@ module Caching
66
67
  end
67
68
  end
68
69
 
70
+ # Explicitly expire the output cached under the given
71
+ # cache key. The cache key is typically the name of the
72
+ # top level action responsible for generating the page.
69
73
  #--
70
74
  # If you change this method, don't forget the CacheSweeper
71
75
  # expire method.
@@ -73,14 +77,20 @@ module Caching
73
77
 
74
78
  def expire_output(name)
75
79
  begin
76
- Logger.debug "Expirinig cache file '#{context.dispatcher.public_root}/#{name}'" if $DBG
77
- FileUtils.rm_rf("#{context.dispatcher.public_root}/#{name}")
78
- rescue Object
80
+ Logger.debug "Expirinig cache file '#{Server.public_root}/#{name}'" if $DBG
81
+ FileUtils.rm_rf("#{Server.public_root}/#{name}")
82
+ rescue Object => ex
79
83
  # gmosx: is this the right thing to do?
80
84
  end
81
85
  end
82
86
  alias_method :delete_output, :expire_output
83
87
 
88
+ # Is caching allowed for this action (page)? The default
89
+ # implementation does not cache post request or request
90
+ # with query parameters. You can work arround the second
91
+ # 'limitation' by cleverly using Nitro's implicit support
92
+ # for 'nice' urls.
93
+
84
94
  def caching_allowed?
85
95
  not (@request.post? or @request.uri =~ /\?/)
86
96
  end
@@ -31,30 +31,28 @@ class Cgi
31
31
  #--
32
32
  # gmosx: only handle nitro requests.
33
33
  #++
34
- if context.path !~ /\./
35
- # gmosx: QUERY_STRING is sometimes not populated.
34
+ # gmosx: QUERY_STRING is sometimes not populated.
36
35
 
37
- if context.query_string.empty? and context.uri =~ /\?/
38
- context.headers['QUERY_STRING'] = context.uri.split('?').last
39
- end
36
+ if context.query_string.empty? and context.uri =~ /\?/
37
+ context.headers['QUERY_STRING'] = context.uri.split('?').last
38
+ end
40
39
 
41
- Cgi.parse_params(context)
42
- Cgi.parse_cookies(context)
43
- context.render(context.path)
40
+ Cgi.parse_params(context)
41
+ Cgi.parse_cookies(context)
42
+ context.render(context.path)
44
43
 
45
- out.print(Cgi.response_headers(context))
46
-
47
- if context.out.is_a?(IO)
48
- begin
49
- while buf = context.out.read(4096)
50
- out.write(buf)
51
- end
52
- ensure
53
- context.out.close
54
- end
55
- else
56
- out.print(context.out)
44
+ out.print(Cgi.response_headers(context))
45
+
46
+ if context.out.is_a?(IO)
47
+ begin
48
+ while buf = context.out.read(4096)
49
+ out.write(buf)
50
+ end
51
+ ensure
52
+ context.out.close
57
53
  end
54
+ else
55
+ out.print(context.out)
58
56
  end
59
57
 
60
58
  $autoreload_dirty = false
@@ -116,7 +114,7 @@ class Cgi
116
114
  if env['HTTP_COOKIE'] or env['COOKIE']
117
115
  (env['HTTP_COOKIE'] or env['COOKIE']).split(/; /).each do |c|
118
116
  key, val = c.split(/=/, 2)
119
-
117
+ val ||= ""
120
118
  key = CGI.unescape(key)
121
119
  val = val.split(/&/).collect{|v| CGI::unescape(v)}.join("\0")
122
120
 
@@ -18,14 +18,18 @@ class Cookie
18
18
  @comment_url = @discard = @port = nil
19
19
  end
20
20
 
21
+ # Set the cookie expiration.
22
+
21
23
  def expires=(t)
22
24
  @expires = t && (t.is_a?(Time) ? t.httpdate : t.to_s)
23
25
  end
24
26
 
27
+ # When the cookie expires.
28
+
25
29
  def expires
26
30
  @expires && Time.parse(@expires)
27
31
  end
28
-
32
+
29
33
  def to_s
30
34
  ret = ""
31
35
  ret << @name << "=" << @value
@@ -26,6 +26,7 @@ module Request
26
26
  # The request cookies.
27
27
 
28
28
  attr_accessor :cookies
29
+ alias_method :cookie, :cookies
29
30
 
30
31
  # The request protocol.
31
32
 
@@ -60,8 +61,8 @@ module Request
60
61
  #
61
62
  # === Examples
62
63
  #
63
- # www.nitrohq.com: request.domain # => 'nitrohq.com'
64
- # www.nitrohq.co.uk: request.domain(2) # => 'nitrohq.co.uk'
64
+ # www.nitroproject.org: request.domain # => 'nitroproject.org'
65
+ # www.nitroproject.co.uk: request.domain(2) # => 'nitroproject.co.uk'
65
66
 
66
67
  def domain(tld_length = 1)
67
68
  host.split('.').last(1 + tld_length).join('.')
@@ -71,7 +72,7 @@ module Request
71
72
  #
72
73
  # === Examples
73
74
  #
74
- # my.name.nitrohq.com: request.subdomains # => ['my', 'name']
75
+ # my.name.nitroproject.org: request.subdomains # => ['my', 'name']
75
76
 
76
77
  def subdomains(tld_length = 1)
77
78
  parts = host.split('.')
@@ -80,7 +81,7 @@ module Request
80
81
 
81
82
  # The request query string.
82
83
  #--
83
- # gmosxi, FIXME: handles some fcgi problems.
84
+ # gmosx, FIXME: handles some fcgi problems.
84
85
  #++
85
86
 
86
87
  def query_string
@@ -251,6 +252,20 @@ module Request
251
252
  @params[param] = value
252
253
  end
253
254
 
255
+ # Check if a boolean param (checkbox) is true.
256
+
257
+ def true?(param)
258
+ @params[param] == 'on'
259
+ end
260
+ alias_method :enabled?, :true?
261
+ alias_method :boolean, :true?
262
+
263
+ # Check if a boolean param (checkbox) is false.
264
+
265
+ def false?(param)
266
+ !true?(param)
267
+ end
268
+
254
269
  # Fetch a parameter with default value.
255
270
 
256
271
  def fetch(param, default = nil)
@@ -263,6 +278,7 @@ module Request
263
278
  @params.has_key?(key)
264
279
  end
265
280
  alias_method :has_param?, :has_key?
281
+ alias_method :param?, :has_key?
266
282
  end
267
283
 
268
284
  end
@@ -40,6 +40,27 @@ class Compiler
40
40
 
41
41
  setting :reload, :default => true, :doc => 'If true all code and templates are reloaded in each request'
42
42
 
43
+
44
+ # The default transformation pipeline. Nitro allows fine
45
+ # grained customization pipelines per controller or even per
46
+ # action. The Template transformation is added by default.
47
+ # Here come examples of pipeline customization:
48
+ #
49
+ # class MyController
50
+ # ann :self, :transformation_pipeline => [MyTransformer, AnotherXForm]
51
+ #
52
+ # ...
53
+ #
54
+ # def my_action
55
+ # ...
56
+ # end
57
+ # ann :my_action, :transformation_pipeline => Compiler.transformation_pipeline.dup.shift(CustomXForm)
58
+ #
59
+ # ...
60
+ # end
61
+
62
+ setting :transformation_pipeline, :default => [StaticInclude, Morphing, Elements, Markup, ScriptCompiler, Cleanup], :doc => 'The default transformation pipeline'
63
+
43
64
  def initialize(controller = nil)
44
65
  @controller = controller
45
66
  @shared = {}
@@ -86,15 +107,34 @@ class Compiler
86
107
  # The default transformation extracts the Ruby code from
87
108
  # processing instructions, and uses the StaticInclude,
88
109
  # Morphing, Elements and Markup compiler modules.
110
+ #
111
+ # The Template transformation stage is added by default.
112
+ #
113
+ # You can override this method or use aspects for really
114
+ # special transformation pipelines. Your imagination is the
115
+ # limit.
116
+ #--
117
+ # TODO: make Template stage pluggable.
118
+ # gmosx: This method is also called from the scaffolding
119
+ # code.
120
+ #++
89
121
 
90
- def transform_template(template)
91
- template = StaticInclude.transform(template, self)
92
- template = Morphing.transform(template, self)
93
- # template = LayoutCompiler.transform(template, self)
94
- template = Elements.transform(template, self)
95
- template = Markup.transform(template)
96
- template = ScriptCompiler.transform(template, self)
97
- template = Cleanup.transform(template)
122
+ def transform_template(action, template)
123
+ # Check for an action specific transformation pipeline.
124
+ if (transformers = @controller.ann(action.to_sym).transformation_pipeline).nil?
125
+ # Check for a controller specific transformation pipeline.
126
+ if (transformers = @controller.ann.self.transformation_pipeline).nil?
127
+ # Use the default transformation pipeline.
128
+ transformers = Compiler.transformation_pipeline
129
+ end
130
+ end
131
+
132
+ transformers.each do |transformer|
133
+ template = transformer.transform(template, self)
134
+ end
135
+
136
+ # Add Template transformation stage by default.
137
+
98
138
  template = Template.transform(template)
99
139
  end
100
140
 
@@ -109,7 +149,7 @@ class Compiler
109
149
 
110
150
  code = %{
111
151
  def #{action}_template
112
- #{transform_template(template)}
152
+ #{transform_template(action, template)}
113
153
  end
114
154
  }
115
155
 
@@ -155,14 +195,19 @@ class Compiler
155
195
  #++
156
196
 
157
197
  def compile_action(action)
158
- action = action.to_s.gsub(/_action$/, '')
198
+ action = @action = action.to_s.gsub(/_action$/, '')
159
199
 
160
200
  return false unless action
161
201
 
162
202
  Logger.debug "Compiling action '#@controller##{action}'" if $DBG
163
203
 
164
204
  valid = false
165
-
205
+
206
+ #--
207
+ # FIXME: parent_action_name does not work as expected,
208
+ # if you include actions from other controllers!!
209
+ #++
210
+
166
211
  code = %{
167
212
  def #{action}_action
168
213
  @parent_action_name = @action_name
@@ -171,7 +216,7 @@ class Compiler
171
216
 
172
217
  # Inject the pre advices.
173
218
 
174
- code << Glue::Aspects.gen_advice_code(action, @controller.advices, :pre)
219
+ code << ::Aspects.gen_advice_code(action, @controller.advices, :pre)
175
220
 
176
221
  # Call the action
177
222
 
@@ -187,16 +232,16 @@ class Compiler
187
232
 
188
233
  # Try to resolve action parameters. Returns negative
189
234
  # numbers for arbitrary parameters.
235
+ #--
236
+ # gmosx: This needs a better implementation. Better means
237
+ # cleaner and more optimized code.
238
+ #++
190
239
 
191
240
  param_count = @controller.instance_method(action.to_sym).arity
192
241
 
193
242
  if param_count != 0
194
- if param_count > 0
195
- code << "params = Array.new(#{param_count}, nil);"
196
- else
197
- code << "params = [];"
198
- end
199
243
  code << %{
244
+ params = []
200
245
  unless context.query_string.blank?
201
246
  all_params = context.query_string.split(/[&;]/)
202
247
  is_hash = all_params.first.index('=')
@@ -208,9 +253,16 @@ class Compiler
208
253
  end
209
254
  code << %{
210
255
  break if qs.index('=') and not is_hash
211
- params[i] = CGI.unescape(qs.split(/=/).last || "")
256
+ params << CGI.unescape(qs.split(/=/).last || "")
212
257
  end
213
258
  end
259
+
260
+ # Concatenate some extracted parameters.
261
+ params.concat(context.params.values)
262
+
263
+ # Fill the array with nils for the missing params.
264
+ (#{param_count} - params.size).times { params << nil }
265
+
214
266
  action_return_value = #{action}(*params)
215
267
  }
216
268
  else
@@ -267,7 +319,7 @@ class Compiler
267
319
 
268
320
  # Inject the post advices.
269
321
 
270
- code << Glue::Aspects.gen_advice_code(action, @controller.advices, :post)
322
+ code << ::Aspects.gen_advice_code(action, @controller.advices, :post)
271
323
 
272
324
  code << %{
273
325
  @action_name = @parent_action_name
@@ -279,6 +331,8 @@ class Compiler
279
331
  @controller.class_eval(code)
280
332
 
281
333
  unless @controller.respond_to?("#{action}_template")
334
+ # If there is not method {action}_template in the controller
335
+ # search for a file template.
282
336
  if template_path
283
337
  compile_template(action, template_path)
284
338
  end
@@ -297,15 +351,6 @@ class Compiler
297
351
  # :section: Helper methods.
298
352
 
299
353
  class << self
300
- # Helper method for manipulating the template transformation
301
- # pipeline.
302
-
303
- def setup_transform_template(&block)
304
- send :define_method, :transform_template, block
305
- end
306
- alias_method :setup_template_transform, :setup_transform_template
307
- alias_method :setup_template_transformation, :setup_transform_template
308
-
309
354
  # Typically used to precompile css templates.
310
355
 
311
356
  def precompile(filename)
@@ -330,3 +375,4 @@ end
330
375
  # * George Moschovitis <gm@navel.gr>
331
376
  # * Chris Farmiloe <chris.farmiloe@farmiloe.com>
332
377
  # * Rob Pitt <rob@motionpath.co.uk>
378
+ # * Jonas Pfenniger <zimba.tm@gmail.com>
@@ -4,7 +4,7 @@ module Nitro
4
4
 
5
5
  module Cleanup
6
6
 
7
- def self.transform(text)
7
+ def self.transform(text, compiler = nil)
8
8
  Glue::Html.cleanup(text)
9
9
  end
10
10
 
@@ -80,7 +80,7 @@ class Elements # :nodoc: all
80
80
  # name = name.demodulize
81
81
  return false unless name =~ PREFIX_RE or name =~ CAPITALIZED_RE
82
82
 
83
- name = name.gsub(/#{PREFIX_RE}:/,'').camelize if name =~ PREFIX_RE
83
+ name = name.gsub(PREFIX_RE,'').camelize if name =~ PREFIX_RE
84
84
 
85
85
  # First try to use Nitro::Element::xxx then ::xxx
86
86
 
@@ -95,7 +95,6 @@ class Elements # :nodoc: all
95
95
  namespace = @compiler.controller.name
96
96
  if /::/ =~ namespace
97
97
  namespace = namespace.gsub( /::[a-zA-Z]+$/, "::#{name}" )
98
- pp namespace
99
98
  klass = Class.by_name(namespace)
100
99
  end
101
100
  rescue
@@ -8,7 +8,7 @@ class Localization
8
8
 
9
9
  # Transform localization macros.
10
10
 
11
- def transform(text)
11
+ def transform(text, compiler = nil)
12
12
  # handle symbols
13
13
  text.gsub!(/\[\[\:(.*?)\]\]/, '#{@lc[\1]}')
14
14
 
@@ -10,7 +10,7 @@ module Markup
10
10
  # Maps #(..) to :sanitize.
11
11
  # Maps #|..| to :markup.
12
12
 
13
- def self.transform(text)
13
+ def self.transform(text, compiler = nil)
14
14
  text.gsub!(/\#\((.*?)\)/, '#{sanitize(\1)}')
15
15
  text.gsub!(/\#\|(.*?)\|/, '#{markup(\1)}')
16
16
  return text