nitro 0.23.0 → 0.24.0

Sign up to get free protection for your applications and to get access to all the features.
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>