nitro 0.29.0 → 0.30.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.
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