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
@@ -1,4 +1,4 @@
1
- require 'nano/object/singleton_class'
1
+ require 'nano/kernel/singleton'
2
2
 
3
3
  require 'nitro/controller'
4
4
  require 'nitro/routing'
@@ -51,7 +51,7 @@ class Dispatcher
51
51
  if controllers and controllers.is_a?(Class) and controllers.ancestors.include?(Controller)
52
52
  controllers = { '/' => controllers }
53
53
  else
54
- controllers ||= { '/' => SimpleController }
54
+ controllers ||= { '/' => Controller }
55
55
  end
56
56
 
57
57
  mount(controllers)
@@ -90,13 +90,17 @@ class Dispatcher
90
90
  # Perform mount-time initialization of the controller.
91
91
 
92
92
  if c.respond_to? :mounted
93
- c.mounted
93
+ c.mounted(path)
94
94
  end
95
95
 
96
96
  # Try to setup a template_root if none is defined:
97
97
 
98
98
  unless c.template_root
99
- c.template_root = "#{Template.root}#{path}".gsub(/\/$/, '')
99
+ c.module_eval %{
100
+ def self.template_root
101
+ "#{Template.root}#{path}".gsub(/\\/$/, '')
102
+ end
103
+ }
100
104
  end
101
105
  end
102
106
 
@@ -130,11 +134,17 @@ class Dispatcher
130
134
 
131
135
  def update_routes
132
136
  @routes = []
137
+
133
138
  @controllers.each do |base, c|
134
139
  base = '' if base == '/'
135
- c.action_metadata.each do |action, meta|
136
- if route = meta[:route]
137
- @routes << [route, "#{base}/#{action}", *meta.params.keys]
140
+ for m in c.action_methods
141
+ if route = c.ann(:m).route and (!route.nil?)
142
+ unless c.ann(:m).params.nil?
143
+ keys = c.ann(:m).params.keys
144
+ else
145
+ keys = []
146
+ end
147
+ @routes << [route, "#{base}/#{action}", *keys]
138
148
  end
139
149
  end
140
150
  end
@@ -167,7 +177,7 @@ class Dispatcher
167
177
  klass = @controllers[key]
168
178
 
169
179
  if klass and Compiler.reload
170
- klass.instance_methods.grep(/(action$)|(template$)/).each do |m|
180
+ klass.instance_methods.grep(/(_action$)|(_template$)/).each do |m|
171
181
  klass.send(:remove_method, m) rescue nil
172
182
  end
173
183
  end
data/lib/nitro/element.rb CHANGED
@@ -1,7 +1,7 @@
1
1
  require 'rexml/document'
2
2
  require 'rexml/streamlistener'
3
3
 
4
- require 'nano/string/capitalized%3F'
4
+ require 'nano/string/capitalized'
5
5
  require 'nano/string/camelize'
6
6
 
7
7
  require 'glue/configuration'
@@ -73,6 +73,8 @@ class Element
73
73
  child._parent = self
74
74
  @_children << child
75
75
  end
76
+
77
+ alias_method :children, :_children
76
78
  end
77
79
 
78
80
  # Processes a page containing elements.
@@ -161,7 +163,9 @@ class ElementProcessor # :nodoc: all
161
163
  def transform(source)
162
164
  listener = Listener.new
163
165
  REXML::Document.parse_stream(source, listener)
164
- # gmosx, FIXME: optimize this, how?
166
+ # gmosx, FIXME: optimize this, how?
167
+ # gmosx, FIXME: this is a hack fix, improve.
168
+ listener.buffer.gsub! /<textarea ([^>]*)><\/textarea>/, '<textarea \1>#{}</textarea>'
165
169
  listener.buffer.gsub! /<(.*) ([^>]*)><\/\1>/, '<\1 \2 />'
166
170
  listener.buffer.gsub! /<(.*)><\/\1>/, '<\1 />'
167
171
  return listener.buffer
data/lib/nitro/flash.rb CHANGED
@@ -6,8 +6,8 @@ module Nitro
6
6
 
7
7
  def self.append_features(base)
8
8
  super
9
- base.pre 'flash.discard'
10
- base.post 'flash.clean'
9
+ base.before 'flash.discard'
10
+ base.after 'flash.clean'
11
11
  end
12
12
 
13
13
  # A Flash is a special hash object that lives in the session.
@@ -22,8 +22,8 @@ private
22
22
 
23
23
  # Output buffers stack, used for php-style nested output
24
24
  # buffering.
25
-
26
- attr :out_buffers
25
+
26
+ def out_buffers; @out_buffers; end
27
27
 
28
28
  # Start (push) a new output buffer.
29
29
 
@@ -1,5 +1,8 @@
1
1
  require 'nano/inflect'
2
2
 
3
+ require 'nitro/mixin/xhtml'
4
+ require 'og/relation/all'
5
+
3
6
  module Nitro
4
7
 
5
8
  # A collection of useful helpers for creating and manipulating
@@ -12,13 +15,22 @@ module Nitro
12
15
  module FormMixin
13
16
 
14
17
  def self.included(base)
18
+ super
15
19
  base.send :include, XhtmlMixin
16
20
  end
17
21
 
18
22
  private
19
23
 
24
+ # Propagate the given parameter by using a form hidden field. The
25
+ # parameter value is tus passed to the next request.
26
+
27
+ def propagate_param(param)
28
+ %|<input type="hidden" name="#{param}" value="#{request[param.to_s]}" />|
29
+ end
30
+ alias_method :keep_param, :propagate_param
31
+
20
32
  # Render a standard form for the given Object. The object
21
- # should include attribute metadata.
33
+ # should include attribute annotations.
22
34
  #--
23
35
  # TODO: get info, for example localization mode from session,
24
36
  # if this module is mixed in a Render.
@@ -28,25 +40,37 @@ private
28
40
  method = options.fetch(:method, 'post')
29
41
  action = options.fetch(:action, "save_#{obj.class.name.underscore}")
30
42
  submit = options.fetch(:submit, 'Save')
31
-
43
+ cancel = options.fetch(:cancel, "#@base/#{Scaffolding.class_to_list(obj.class)}")
44
+
45
+ if enctype = options.fetch(:enctype, nil)
46
+ enctype_attribute = " enctype=\"#{enctype}\""
47
+ else
48
+ enctype_attribute = ''
49
+ for pr in obj.class.properties.values
50
+ if pr.klass.ancestors.include? Og::Blob
51
+ enctype_attribute = ' enctype="multipart/form-data"'
52
+ end
53
+ end
54
+ end
55
+
32
56
  action = "#{@base}/#{action}" unless action =~ /\//
33
57
 
34
58
  str = %{<form action="#{action}" method="post">}
35
- if obj.oid
36
- str << %{
37
- <input type="hidden" name="oid" value="#{obj.oid}" />}
38
- end
39
- str << %{
40
- #{tags_for(obj, options)}
41
- <br />
42
- <input type="submit" value="#{submit}" /> or <a href="#@base/#{Scaffolding.class_to_list(obj.class)}">Cancel</a>
43
- </form>
44
- }
59
+
60
+ str = ''
61
+
62
+ str << %{<form action="#{action}" method="post"#{enctype_attribute}>}
63
+ str << %{<input type="hidden" name="oid" value="#{obj.oid}" />} if obj.oid
64
+ str << tags_for(obj, options)
65
+ str << %{<input type="submit" value="#{submit}" />}
66
+ str << %{ or <a href="#{cancel}">Cancel</a>} if cancel
67
+ str << %{</form>}
68
+
45
69
  return str
46
70
  end
47
71
 
48
72
  # Render a standard form tags for the given Object. The object
49
- # should include attribute metadata.
73
+ # should include attribute annotations.
50
74
  #
51
75
  # If show_all is false then apply field filtering.
52
76
  #
@@ -65,113 +89,200 @@ private
65
89
  def tags_for(obj, options = {})
66
90
  str = prologue()
67
91
 
68
- for p in obj.class.properties
69
- next if :oid == p.symbol unless options[:all]
92
+ for p in obj.class.properties.values
93
+ unless options[:all]
94
+ next if :oid == p.symbol or p.editor == :none #TODO check for real key
95
+ end
96
+
70
97
  ancestors = p.klass.ancestors
71
- if ancestors.include?(Numeric)
72
- unless p.meta[:relation]
73
- str << field_tag(obj, p, options)
74
- end
75
- elsif ancestors.include?(String)
76
- if p.metadata[:editor] == :textarea
77
- str << textarea_tag(obj, p, options)
98
+
99
+ tag = if p.editor and obj.respond_to?(p.editor.to_sym)
100
+ obj.send(p.editor.to_sym, p, options) #farms: send to custom _instance_ method
101
+ elsif ancestors.include? Numeric and !p.relation
102
+ field_tag(obj, p, options)
103
+ elsif ancestors.include? String
104
+ if p.editor == :textarea
105
+ textarea_tag(obj, p, options)
78
106
  else
79
- str << field_tag(obj, p, options)
107
+ field_tag(obj, p, options)
80
108
  end
81
- elsif ancestors.include?(TrueClass)
82
- str << checkbox_tag(obj, p, options)
83
- elsif ancestors.include?(Time)
84
- str << datetime_tag(obj, p, options)
109
+ elsif ancestors.include? TrueClass
110
+ checkbox_tag(obj, p, options)
111
+ elsif ancestors.include? Date
112
+ date_tag(obj, p, options)
113
+ elsif ancestors.include? Time
114
+ datetime_tag(obj, p, options)
115
+ elsif ancestors.include? Og::Blob
116
+ file_tag(obj, p, options)
117
+ elsif ancestors.include? Hash
118
+ hash_tag(obj, p, options)
119
+ else
120
+ nil #no tag
121
+ end
122
+
123
+ unless tag.nil?
124
+ #farms: TODO. way to override the general 'element' and disable per prop
125
+ str << element(p.symbol, tag)
85
126
  end
86
127
  end
87
128
 
88
- for rel in obj.class.relations
89
- case rel
90
- when Og::BelongsTo
91
- str << belongs_to_tag(obj, rel, options)
92
- when Og::HasMany
93
- str << has_many_tag(obj, rel, options)
129
+ if obj.class.relations and not options[:skip_relations]
130
+ for rel in obj.class.relations
131
+ next if rel.options[:editor] == :none unless options[:all]
132
+
133
+ tag = if rel.options[:editor] && obj.respond_to?(rel.options[:editor].to_sym)
134
+ obj.send(rel.options[:editor].to_sym, rel, options) #farms: allow custom relation editors
135
+ elsif rel.kind_of? Og::RefersTo
136
+ refers_to_control(obj, rel, options)
137
+ elsif rel.kind_of? Og::JoinsMany or rel.kind_of? Og::HasMany
138
+ if rel.options[:editor] == :twin_select or rel.options[:editor] == :twin
139
+ many_select_control(obj, rel, options)
140
+ else
141
+ many_list_control(obj, rel, options)
142
+ end
143
+ else
144
+ nil #no tag
145
+ end
146
+
147
+ unless tag.nil?
148
+ #farms: TODO. way to override the general 'element' and disable per rel
149
+ str << element(rel.name, tag)
150
+ end
94
151
  end
95
152
  end
96
-
153
+
97
154
  str << epilogue()
98
155
 
99
156
  return str
100
157
  end
101
158
 
102
- def belongs_to_tag(obj, rel, options)
103
- entities = rel.target_class.all
104
- labels = entities.map { |e| e.to_s }
105
- values = entities.map { |e| e.oid }
106
- element(
107
- label(rel.name),
108
- %{
109
- <select id="#{rel.name}" name="#{rel.name}">
110
- #{options(:labels => labels, :values => values, :selected => 1)}
111
- </select>
112
- }
113
- )
114
- end
115
-
116
- def has_many_tag(obj, rel, options)
117
- entities = obj.send(rel.target_plural_name) if obj.saved?
118
- unless entities.empty?
119
- str = entities.inject('') do |acc, e|
120
- acc << "<tr><td>#{e.to_edit_link(@base)}</td></tr>"
121
- end
122
- str = "<table>#{str}</table>"
123
- else
124
- str = 'No entities found.<br /><br />'
159
+ def hash_tag(obj, p, options)
160
+ str = %{
161
+ <input type="text" id="#{p.symbol}_key" /> =&gt; <input type="text" id="#{p.symbol}_value" />
162
+ <input id="#{p.symbol}" class="naction_add_hash" type="button" value=" + " />
163
+ <ul id="#{p.symbol}_list">
164
+ }
165
+ obj.send(p.symbol).each do |k,v|
166
+ str << hash_tag_item(p.symbol, k, v)
125
167
  end
126
- element(
127
- label(rel.name) + '&nbsp;<a href="#">Add</a>',
128
- %{
129
- #{str}
130
- }
131
- )
168
+ str << %{</ul>}
132
169
  end
133
-
134
- def field_tag(obj, p, options)
170
+
171
+ def hash_tag_item(name, key='', value='')
135
172
  %{
136
- <dt>#{label(p.symbol)}</dt>
137
- <dd>
138
- <input type="text" id="#{p.symbol}" name="#{p.symbol}" value="#{obj.send(p.symbol)}" style="width: 250px" />
139
- </dd>
173
+ <li>
174
+ <input type="text" value="#{key}" disabled="disabled" class="hash_item_key" /> =&gt; <input type="text" name="#{name}[#{key}]" id="#{name}_value" value="#{value}" class="hash_item_value" />
175
+ <span class="remove"><a href="#" class="naction_remove_hash" id="#{name}_remove">remove</a></span>
176
+ </li>
140
177
  }
141
178
  end
179
+ public :hash_tag_item # Allow ajax access
142
180
 
181
+ def file_tag(obj, p, options)
182
+ %{<input type="file" id="#{p.symbol}" name="#{p.symbol}" style="width: 250px" />}
183
+ end
184
+
185
+ def field_tag(obj, p, options)
186
+ %{<input type="text" id="#{p.symbol}" name="#{p.symbol}" value="#{obj.send(p.symbol)}" style="width: 250px" /> }
187
+ end
188
+
143
189
  def textarea_tag(obj, p, options)
144
- %{
145
- <dt>#{label(p.symbol)}</dt>
146
- <dd>
147
- <textarea id="#{p.symbol}" name="#{p.symbol}">#{obj.send(p.symbol)}</textarea>
148
- </dd>
149
- }
190
+ %{<textarea id="#{p.symbol}" name="#{p.symbol}">#{obj.send(p.symbol)}</textarea> }
150
191
  end
151
192
 
152
193
  def checkbox_tag(obj, p, options)
153
- %{
154
- <dt>
155
- <input type="checkbox" id="#{p.symbol}" name="#{p.symbol}" />&nbsp;
156
- #{label(p.symbol)}
157
- </dt>
158
- <dd>#{}</dd>
159
- }
194
+ checked_flag = obj.send(p.symbol) ? 'checked="checked" ' : ''
195
+ %{<input type="checkbox" id="#{p.symbol}" name="#{p.symbol}" #{checked_flag}/>&nbsp; }
160
196
  end
161
-
197
+
198
+ def date_tag(obj, p, options)
199
+ date_select(obj.send(p.symbol), :name => p.symbol.to_s)
200
+ end
201
+
162
202
  def datetime_tag(obj, p, options)
163
- element(
164
- label(p.symbol),
165
- datetime_select(obj.send(p.symbol), :name => p.symbol.to_s)
166
- )
203
+ datetime_select(obj.send(p.symbol), :name => p.symbol.to_s)
167
204
  end
168
205
 
169
206
  def label(sym)
170
- %|<label for="#{sym}">#{sym.to_s.humanize}</label>|
207
+ %{<label for="#{sym}">#{sym.to_s.humanize}</label>}
171
208
  end
172
209
 
173
210
  # :section: Relations.
174
211
 
212
+ def refers_to_control(obj, rel, options)
213
+ entities = rel.target_class.all
214
+ selected = obj.send(rel.name)
215
+ selected = selected.pk if selected
216
+ unless entities.empty?
217
+ str = %{<select id="#{rel.name}" name="#{rel.name}">}
218
+ str << %{<option value="null">None</option>} if rel.options[:allow_none]
219
+ str << %{#{options(:labels => entities.map{|e| e.to_s}, :values => entities.map{|e| e.oid}, :selected => selected)}</select>}
220
+ else
221
+ 'No entities found.<br /><br />'
222
+ end
223
+ end
224
+
225
+ def many_list_control(obj, rel, options)
226
+ entities = rel.target_class.all
227
+
228
+ unless entities.empty?
229
+ selected_entities = obj.send(rel.name)
230
+ selector_entities = entities.delete_if {|e| obj.saved? && selected_entities.include?(e)}
231
+
232
+ str = %{
233
+ <select id="#{rel.name}_selector">
234
+ #{options(:labels => selector_entities.map{|e| e.to_s}, :values => selector_entities.map{|e| e.oid})}
235
+ </select>
236
+ <input id="#{rel.name}" type="button" value=" + " class="naction_add_rel" />
237
+ <ul class="relation_list" id="#{rel.name}_list">
238
+ }
239
+ selected_entities.each do |e|
240
+ str << relation_item(rel.name, e.to_s, e.oid)
241
+ end
242
+ str << %{</ul>}
243
+ else
244
+ 'No entities found.<br /><br />'
245
+ end
246
+ end
247
+
248
+ def relation_item(name, display, oid)
249
+ %{
250
+ <li>
251
+ <input type="hidden" name="#{name}[]" value="#{oid}" />
252
+ <span class="display">#{display}</span>
253
+ <span class="remove"><a href="#" id="#{name}_remove" class="naction_remove_rel">remove</a></span>
254
+ </li>
255
+ }
256
+ end
257
+ public :relation_item # Allow ajax access.
258
+
259
+ def many_select_control(obj, rel, options)
260
+ entities = rel.target_class.all
261
+
262
+ unless entities.empty?
263
+ selected_entities = obj.send(rel.name)
264
+ selector_entities = entities.delete_if {|e| obj.saved? && selected_entities.include?(e)}
265
+ str = %{
266
+ <div id="#{rel.name}" class="has_many_control">
267
+ <select id="#{rel.name}_selector" multiple="multiple">
268
+ #{options(:labels => selector_entities.map{|e| e.to_s}, :values => selector_entities.map{|e| e.oid})}
269
+ </select>
270
+
271
+ <a href="javascript:multiSelectorMove('#{rel.name}','add');">add &gt;&gt;</a>
272
+ <a href="javascript:multiSelectorMove('#{rel.name}','remove');">&lt;&lt; remove</a>
273
+
274
+ <select id="#{rel.name}_selected" multiple="multiple">
275
+ #{options(:labels => selected_entities.map{|e| e.to_s}, :values => selected_entities.map{|e| e.oid})}
276
+ </select>
277
+ }
278
+ selected_entities.each do |e|
279
+ str << %{<input type="hidden" name="#{rel.name}[]" id="#{rel.name}_#{e.oid}" value="#{e.oid}" />}
280
+ end
281
+ str << %{</div>}
282
+ else
283
+ 'No entities found.<br /><br />'
284
+ end
285
+ end
175
286
 
176
287
  # :section: General formating methods. Override to customize.
177
288
 
@@ -189,12 +300,10 @@ private
189
300
 
190
301
  # Emit a form element.
191
302
 
192
- def element(label, element)
303
+ def element(name, tag)
193
304
  %{
194
- <dt>#{label}</dt>
195
- <dd>
196
- #{element}
197
- </dd>
305
+ <dt class="#{name}">#{label(name)}</dt>
306
+ <dd class="#{name}">#{tag}</dd>
198
307
  }
199
308
  end
200
309
  end
@@ -202,3 +311,5 @@ end
202
311
  end
203
312
 
204
313
  # * George Moschovitis <gm@navel.gr>
314
+ # * Chris Farmiloe <chris.farmiloe@farmiloe.com>
315
+ # * Rob Pitt <rob@motionpath.co.uk>