nitro 0.23.0 → 0.24.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 (76) hide show
  1. data/CHANGELOG +350 -0
  2. data/INSTALL +2 -2
  3. data/ProjectInfo +61 -0
  4. data/README +5 -4
  5. data/Rakefile +5 -4
  6. data/bin/nitrogen +3 -1
  7. data/doc/AUTHORS +27 -3
  8. data/doc/RELEASES +193 -0
  9. data/doc/lhttpd.txt +4 -0
  10. data/lib/nitro.rb +1 -1
  11. data/lib/nitro/adapter/cgi.rb +6 -321
  12. data/lib/nitro/adapter/fastcgi.rb +2 -14
  13. data/lib/nitro/adapter/scgi.rb +237 -71
  14. data/lib/nitro/adapter/webrick.rb +25 -7
  15. data/lib/nitro/caching.rb +1 -0
  16. data/lib/nitro/cgi.rb +296 -0
  17. data/lib/nitro/{cookie.rb → cgi/cookie.rb} +0 -0
  18. data/lib/nitro/cgi/http.rb +62 -0
  19. data/lib/nitro/{request.rb → cgi/request.rb} +4 -1
  20. data/lib/nitro/{response.rb → cgi/response.rb} +0 -0
  21. data/lib/nitro/cgi/stream.rb +43 -0
  22. data/lib/nitro/cgi/utils.rb +38 -0
  23. data/lib/nitro/compiler.rb +23 -11
  24. data/lib/nitro/compiler/css.rb +8 -0
  25. data/lib/nitro/compiler/morphing.rb +66 -0
  26. data/lib/nitro/context.rb +21 -30
  27. data/lib/nitro/controller.rb +23 -100
  28. data/lib/nitro/dispatcher.rb +18 -8
  29. data/lib/nitro/element.rb +6 -2
  30. data/lib/nitro/flash.rb +2 -2
  31. data/lib/nitro/mixin/buffer.rb +2 -2
  32. data/lib/nitro/mixin/form.rb +204 -93
  33. data/lib/nitro/mixin/javascript.rb +170 -11
  34. data/lib/nitro/mixin/markup.rb +1 -0
  35. data/lib/nitro/mixin/pager.rb +7 -4
  36. data/lib/nitro/mixin/rss.rb +2 -0
  37. data/lib/nitro/mixin/table.rb +23 -6
  38. data/lib/nitro/mixin/xhtml.rb +2 -2
  39. data/lib/nitro/render.rb +19 -5
  40. data/lib/nitro/scaffold.rb +12 -6
  41. data/lib/nitro/server.rb +4 -6
  42. data/lib/nitro/server/runner.rb +2 -2
  43. data/lib/nitro/session.rb +8 -1
  44. data/lib/nitro/session/file.rb +40 -0
  45. data/lib/part/admin.rb +2 -0
  46. data/lib/part/admin/controller.rb +7 -3
  47. data/lib/part/admin/skin.rb +8 -1
  48. data/lib/part/admin/template/index.xhtml +39 -1
  49. data/proto/public/error.xhtml +5 -3
  50. data/proto/public/js/behaviour.js +254 -254
  51. data/proto/public/js/controls.js +427 -165
  52. data/proto/public/js/dragdrop.js +255 -276
  53. data/proto/public/js/effects.js +476 -277
  54. data/proto/public/js/prototype.js +561 -127
  55. data/proto/public/js/scaffold.js +74 -0
  56. data/proto/public/js/scriptaculous.js +44 -0
  57. data/proto/public/js/util.js +548 -0
  58. data/proto/public/scaffold/list.xhtml +4 -1
  59. data/proto/scgi.rb +333 -0
  60. data/script/scgi_ctl +221 -0
  61. data/script/scgi_service +120 -0
  62. data/test/nitro/adapter/raw_post1.bin +0 -0
  63. data/test/nitro/{tc_cookie.rb → cgi/tc_cookie.rb} +1 -1
  64. data/test/nitro/{tc_request.rb → cgi/tc_request.rb} +1 -1
  65. data/test/nitro/mixin/tc_xhtml.rb +1 -1
  66. data/test/nitro/{adapter/tc_cgi.rb → tc_cgi.rb} +12 -12
  67. data/test/nitro/tc_controller.rb +9 -5
  68. metadata +159 -169
  69. data/benchmark/bench.rb +0 -5
  70. data/benchmark/simple-webrick-n-200.txt +0 -44
  71. data/benchmark/static-webrick-n-200.txt +0 -43
  72. data/benchmark/tiny-lhttpd-n-200-c-5.txt +0 -43
  73. data/benchmark/tiny-webrick-n-200-c-5.txt +0 -44
  74. data/benchmark/tiny-webrick-n-200.txt +0 -44
  75. data/benchmark/tiny2-webrick-n-200.txt +0 -44
  76. data/examples/README +0 -7
@@ -28,11 +28,15 @@ private
28
28
  def behaviour(id, js)
29
29
  @_behaviours ||= []
30
30
  @_behaviours << [id, js]
31
+ return nil
31
32
  end
32
33
 
33
34
  # :section: prototype.js
34
35
 
35
36
  def live_request(id, options = {})
37
+ __append_script_file__ 'js/behaviour.js'
38
+ __append_script_file__ 'js/prototype.js'
39
+
36
40
  if href = options.delete(:href)
37
41
  behaviour "##{id}", %{
38
42
  el.onclick = function() {
@@ -48,6 +52,7 @@ private
48
52
  }
49
53
  }
50
54
  end
55
+ return nil
51
56
  end
52
57
  alias_method :live, :live_request
53
58
  alias_method :async, :live_request
@@ -57,8 +62,13 @@ private
57
62
  # Make the element dragable.
58
63
 
59
64
  def draggable(id, options = {})
60
- @_javascript ||= ''
61
- @_javascript << "\nnew Draggable('#{id}', #{hash_to_js(options)});"
65
+ __append_script_file__ 'js/behaviour.js'
66
+ __append_script_file__ 'js/prototype.js'
67
+ __append_script_file__ 'js/effects.js'
68
+ __append_script_file__ 'js/dragdrop.js'
69
+
70
+ __append_script__ "\nnew Draggable('#{id}', #{hash_to_js(options)});"
71
+ return nil
62
72
  end
63
73
 
64
74
  # :section: script.aculo.us controls.js
@@ -68,8 +78,13 @@ private
68
78
  def auto_complete(id, options = {})
69
79
  update = options[:update] || "#{id}_auto_complete"
70
80
  url = options[:url] || "#{id}_auto_complete"
71
- @_javascript ||= ''
72
- @_javascript << "\nnew Ajax.Autocompleter('#{id}', '#{update}', '#{url}');"
81
+
82
+ __append_script_file__ 'js/behaviour.js'
83
+ __append_script_file__ 'js/prototype.js'
84
+ __append_script_file__ 'js/effects.js'
85
+ __append_script_file__ 'js/controls.js'
86
+
87
+ __append_script__ "\nnew Ajax.Autocompleter('#{id}', '#{update}', '#{url}');"
73
88
 
74
89
  # Turn off the browser's autocomplete functionality to avoid
75
90
  # interference.
@@ -77,18 +92,127 @@ private
77
92
  behaviour "##{id}", %{
78
93
  el.autocomplete = 'off';
79
94
  }
95
+ return nil
96
+ end
97
+
98
+ # :section: styling.
99
+
100
+ # Style border.
101
+
102
+ def style_border
103
+ end
104
+
105
+ # Round element corners using the nifty corners technique.
106
+
107
+ def round_corners(id, options = {})
108
+ o = {
109
+ :bg => '#fff',
110
+ :fg => '#ccc',
111
+ }.update(options)
112
+
113
+ __append_script_file__ 'js/prototype.js'
114
+ __append_script_file__ 'js/round.js'
115
+
116
+ __append_script__ %{
117
+ Box.round('#{id}', '#{o[:fg]}', '#{o[:bg]}');
118
+ }
119
+
120
+ __append_css__ %{
121
+ .rtop,.rbottom{display:block}
122
+ .rtop *,.rbottom *{display:block;height: 1px;overflow: hidden}
123
+ .r1{margin: 0 5px}
124
+ .r2{margin: 0 3px}
125
+ .r3{margin: 0 2px}
126
+ .r4{margin: 0 1px;height: 2px}
127
+ }
128
+
129
+ return nil
130
+ end
131
+
132
+ # Generalized border decoration.
133
+
134
+ def decorate_borders(options = {})
135
+ o = {
136
+ :bg => '#fff',
137
+ :fg => '#ccc',
138
+ :klass => 'w'
139
+ }.update(options)
140
+
141
+ klass = o[:klass]
142
+
143
+ __append_script_file__ 'js/prototype.js'
144
+ __append_script_file__ 'js/styler.js'
145
+
146
+ __append_script__ %{
147
+ decorateBorders('#{klass}');
148
+ }
149
+
150
+ __append_css__ %{
151
+ .#{klass}t {
152
+ background: #{o[:bg]};
153
+ background-image: url(m/#{klass}t.gif);
154
+ background-repeat: repeat-x;
155
+ background-position: top;
156
+ }
157
+ .#{klass}r {
158
+ background-image: url(m/#{klass}r.gif);
159
+ background-repeat: repeat-y;
160
+ background-position: right;
161
+ }
162
+ .#{klass}b {
163
+ background-image: url(m/#{klass}b.gif);
164
+ background-repeat: repeat-x;
165
+ background-position: bottom;
166
+ }
167
+ .#{klass}l {
168
+ background-image: url(m/#{klass}l.gif);
169
+ background-repeat: repeat-y;
170
+ background-position: left;
171
+ }
172
+ .#{klass}tl {
173
+ background-image: url(m/#{klass}tl.gif);
174
+ background-repeat: no-repeat;
175
+ background-position: top left;
176
+ }
177
+ .#{klass}tr {
178
+ background-image: url(m/#{klass}tr.gif);
179
+ background-repeat: no-repeat;
180
+ background-position: top right;
181
+ }
182
+ .#{klass}bl {
183
+ background-image: url(m/#{klass}bl.gif);
184
+ background-repeat: no-repeat;
185
+ background-position: bottom left;
186
+ }
187
+ .#{klass}br {
188
+ background-image: url(m/#{klass}br.gif);
189
+ background-repeat: no-repeat;
190
+ background-position: bottom right;
191
+ }
192
+ .#{klass}c {
193
+ padding: 25px;
194
+ }
195
+ }
196
+
197
+ return nil
80
198
  end
81
199
 
82
200
  # :section: general javascript helpers.
83
201
 
84
202
  # Include external javascript file.
85
203
 
86
- def include_script(files = DEFAULT_JAVASCRIPT_FILES)
87
- code = ''
204
+ def include_script(*files)
205
+ return if @_script_files.nil? and files.empty?
88
206
 
89
- for file in [files].flatten
90
- code << %|<script src="#{file}" type="text/javascript">//</script>|
91
- end
207
+ code = ''
208
+
209
+ for file in files
210
+ code << %|<script src="#{file}" type="text/javascript">//</script>\n|
211
+ end if files
212
+
213
+ for file in @_script_files
214
+ code << %|<script src="#{file}" type="text/javascript">//</script>\n|
215
+ end if @_script_files
92
216
 
93
217
  return code
94
218
  end
@@ -105,12 +229,20 @@ private
105
229
  '{' + options.map {|k, v| "#{k}:#{v}"}.join(', ') + '}'
106
230
  end
107
231
 
232
+ # Emits the aggregated css.
233
+
234
+ def emit_css
235
+ return unless @_css
236
+ %{<style>#{@_css.join("\n")}</style>}
237
+ end
238
+ alias_method :helper_css, :emit_css
239
+
108
240
  # Emits the aggregated helper javascript.
109
241
  #--
110
242
  # FIXME: refactor this!
111
243
  #++
112
244
 
113
- def helper_script
245
+ def emit_script
114
246
  code = %|<script type="text/javascript">\n|
115
247
  unless @_behaviours.empty?
116
248
  code << %|var _behaviours = {\n|
@@ -125,11 +257,38 @@ private
125
257
  |
126
258
  end
127
259
  code << %|
128
- #@_javascript
260
+ #{@_script.join("\n")}
261
+ | if @_script
262
+ code << %|
129
263
  </script>
130
264
  |
131
265
  end
266
+ alias_method :helper_script, :emit_script
267
+
268
+ # ...
132
269
 
270
+ def js_distance_of_time_in_words(time)
271
+ time = time.utc.strftime("%a, %d %b %Y %H:%M:%S GMT")
272
+ %|<span class="human_time" title="#{time}">#{time}</span>|
273
+ end
274
+
275
+ # :section: internal helpers.
276
+
277
+ def __append_script_file__(file)
278
+ @_script_files ||= []
279
+ @_script_files << file unless @_script_files.include?(file)
280
+ end
281
+
282
+ def __append_script__(script)
283
+ @_script ||= []
284
+ @_script << script unless @_script.include?(script)
285
+ end
286
+
287
+ def __append_css__(css)
288
+ @_css ||= []
289
+ @_css << css unless @_css.include?(css)
290
+ end
291
+
133
292
  end
134
293
 
135
294
  end
@@ -85,6 +85,7 @@ private
85
85
  end
86
86
 
87
87
  def escape(str)
88
+ return nil unless str
88
89
  CGI.escape(str.gsub(/ /, '_'))
89
90
  end
90
91
 
@@ -1,6 +1,8 @@
1
1
  require 'glue/uri'
2
2
  require 'glue/configuration'
3
3
 
4
+ require 'og/collection'
5
+
4
6
  module Nitro
5
7
 
6
8
  # Displays a collection of entitities in multiple pages.
@@ -258,24 +260,25 @@ private
258
260
 
259
261
  def paginate(items, options = {})
260
262
  per_page = options.delete(:per_page) || Pager.per_page
261
-
263
+ pager_key = options.delete(:pager_key) || Pager.key
264
+
262
265
  case items
263
266
  when Array
264
267
  items = items.dup
265
- pager = Pager.new(request, per_page, items.size, key = Pager.key)
268
+ pager = Pager.new(request, per_page, items.size, pager_key)
266
269
  items = items.slice(pager.offset, pager.per_page)
267
270
  return items, pager
268
271
 
269
272
  when Og::Collection
270
273
  collection = items
271
- pager = Pager.new(request, per_page, collection.count, key = Pager.key)
274
+ pager = Pager.new(request, per_page, collection.count, pager_key)
272
275
  options.update(pager.limit)
273
276
  items = collection.reload(options)
274
277
  return items, pager
275
278
 
276
279
  when Class
277
280
  klass = items
278
- pager = Pager.new(request, per_page, klass.count(options), key = Pager.key)
281
+ pager = Pager.new(request, per_page, klass.count(options), pager_key)
279
282
  options.update(pager.limit)
280
283
  items = klass.all(options)
281
284
  return items, pager
@@ -32,6 +32,7 @@ module RssMixin
32
32
 
33
33
  def build_rss_09(objects, options = {})
34
34
  c = {
35
+ :title => 'Syndication',
35
36
  :description => 'Syndication'
36
37
  }.update(options)
37
38
 
@@ -40,6 +41,7 @@ module RssMixin
40
41
  raise "Option ':base' cannot be infered!" unless c[:base]
41
42
 
42
43
  channel = RSS::Rss::Channel.new
44
+ channel.title = c[:title]
43
45
  channel.description = c[:description]
44
46
  channel.link = c[:link] if c[:link]
45
47
 
@@ -8,11 +8,11 @@ module Nitro
8
8
  #
9
9
  # <?r
10
10
  # users = User.all.map { |u| [u.name, u.first_name, u.last_name, u.email] }
11
- # header = ['Username', 'First name', 'Last name', 'Email']
11
+ # headers = ['Username', 'First name', 'Last name', 'Email']
12
12
  # ?>
13
13
  #
14
14
  # <div class="custom-table-class">
15
- # #{table(users, header)}
15
+ # #{table :values => users, :headers => header}
16
16
  # </div>
17
17
  #--
18
18
  # TODO: sorting, thead/tbody/legend etc, verbose...
@@ -32,7 +32,25 @@ module TableMixin
32
32
 
33
33
  str = '<table'
34
34
  str << %| id="#{c[:id]}"| if c[:id]
35
- str << '><tr>'
35
+ str << '>'
36
+
37
+ str << table_rows(options)
38
+
39
+ str << '</table>'
40
+ end
41
+ alias_method :build_table, :table
42
+
43
+ # [+options+]
44
+ # A hash of options.
45
+ #
46
+ # :id = id of the component.
47
+ # :headers = an array of the header values
48
+ # :values = an array of arrays.
49
+
50
+ def table_rows(options)
51
+ c = options
52
+
53
+ str = '<tr>'
36
54
 
37
55
  for h in c[:headers]
38
56
  str << %|<th>#{h}</th>|
@@ -40,6 +58,8 @@ module TableMixin
40
58
 
41
59
  str << "</tr>"
42
60
 
61
+ items = c[:values] || c[:items] || c[:rows]
62
+
43
63
  for row in c[:values]
44
64
  str << "<tr>"
45
65
 
@@ -50,11 +70,8 @@ module TableMixin
50
70
  str << "</tr>"
51
71
  end
52
72
 
53
- str << "</table>"
54
-
55
73
  return str
56
74
  end
57
- alias_method :build_table, :table
58
75
 
59
76
  end
60
77
 
@@ -90,7 +90,7 @@ private
90
90
  #{options(:labels_values => (1..31).to_a, :selected => date.day)}
91
91
  </select>&nbsp;
92
92
  <select id="#{name}.month" name="#{name}.month">
93
- #{options(:labels => Date::MONTHNAMES, :values => (1..12).to_a, :selected => (date.month+1))}
93
+ #{options(:labels => Date::MONTHNAMES, :values => (1..12).to_a, :selected => (date.month))}
94
94
  </select>&nbsp;
95
95
  <select id="#{name}.year" name="#{name}.year">
96
96
  #{options(:labels_values => ((Time.now.year-10)..(Time.now.year+10)).to_a, :selected => date.year)}
@@ -129,7 +129,7 @@ private
129
129
  :scrollbars => false,
130
130
  }.merge(options)
131
131
 
132
- poptions = (o[:resizable] ? 'resizable=yes' : 'resizable=no')
132
+ poptions = (o[:resizable] ? 'resizable=yes,' : 'resizable=no,')
133
133
  poptions << (o[:scrollbars] ? 'scrollbars=yes' : 'scrollbars=no')
134
134
 
135
135
  url = o[:url] || o[:uri]
data/lib/nitro/render.rb CHANGED
@@ -1,7 +1,8 @@
1
1
  require 'singleton'
2
2
  require 'sync'
3
+ require 'stringio'
3
4
 
4
- require 'nano/string/blank%3F'
5
+ require 'nano/string/blank'
5
6
 
6
7
  require 'glue/attribute'
7
8
  require 'glue/settings'
@@ -21,7 +22,7 @@ module Nitro
21
22
 
22
23
  class ActionExit < Exception; end
23
24
 
24
- # Raise or Thorw this exception to stop rendering altogether.
25
+ # Raise or Throw this exception to stop rendering altogether.
25
26
  # Typically called by redirects.
26
27
 
27
28
  class RenderExit < Exception; end
@@ -121,10 +122,10 @@ module Render
121
122
  if self.class == klass
122
123
  self.send(action)
123
124
  else
124
- klass.new(self, base).send(action)
125
+ klass.new(@context, base).send(action)
125
126
  end
126
127
 
127
- rescue RenderExit => e
128
+ rescue RenderExit, ActionExit => e
128
129
 
129
130
  # Just stop rendering.
130
131
  # For example called by redirects.
@@ -140,6 +141,19 @@ module Render
140
141
 
141
142
  private
142
143
 
144
+ # Helper method to exit the current action, typically used
145
+ # to skip the template.
146
+
147
+ def exit
148
+ raise ActionExit.new
149
+ end
150
+
151
+ # Flush the IO object if we are in streaming mode.
152
+
153
+ def flush
154
+ @out.flush if @out.is_a?(IO)
155
+ end
156
+
143
157
  # Send a redirect response.
144
158
  #
145
159
  # If the url starts with '/' it is considered absolute, else
@@ -197,7 +211,7 @@ private
197
211
 
198
212
  def render_template(filename)
199
213
  filename = "#{filename}.xhtml" unless filename =~ /\.xhtml$/
200
- template = File.read("#{self.class.template_root}/#{filename}")
214
+ template = File.read("#{template_root}/#{filename}")
201
215
  Template.process_template(template, '@out', binding)
202
216
  end
203
217