nitro 0.25.0 → 0.26.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 +531 -1
  2. data/ProjectInfo +29 -5
  3. data/README +1 -1
  4. data/doc/AUTHORS +12 -6
  5. data/doc/RELEASES +114 -0
  6. data/lib/glue/sweeper.rb +71 -0
  7. data/lib/nitro.rb +19 -12
  8. data/lib/nitro/adapter/cgi.rb +4 -0
  9. data/lib/nitro/adapter/webrick.rb +4 -2
  10. data/lib/nitro/caching.rb +1 -0
  11. data/lib/nitro/caching/fragments.rb +7 -1
  12. data/lib/nitro/caching/output.rb +6 -1
  13. data/lib/nitro/caching/stores.rb +13 -1
  14. data/lib/nitro/cgi.rb +9 -1
  15. data/lib/nitro/cgi/request.rb +11 -3
  16. data/lib/nitro/cgi/utils.rb +24 -2
  17. data/lib/nitro/compiler.rb +89 -63
  18. data/lib/nitro/compiler/cleanup.rb +16 -0
  19. data/lib/nitro/compiler/elements.rb +117 -0
  20. data/lib/nitro/compiler/markup.rb +3 -1
  21. data/lib/nitro/compiler/morphing.rb +203 -73
  22. data/lib/nitro/compiler/script_generator.rb +14 -0
  23. data/lib/nitro/compiler/shaders.rb +1 -1
  24. data/lib/nitro/context.rb +5 -6
  25. data/lib/nitro/controller.rb +43 -21
  26. data/lib/nitro/dispatcher.rb +86 -37
  27. data/lib/nitro/element.rb +3 -105
  28. data/lib/nitro/helper/benchmark.rb +3 -0
  29. data/lib/nitro/helper/dojo.rb +0 -0
  30. data/lib/nitro/helper/form.rb +85 -255
  31. data/lib/nitro/helper/form/controls.rb +274 -0
  32. data/lib/nitro/helper/javascript.rb +86 -6
  33. data/lib/nitro/helper/pager.rb +5 -0
  34. data/lib/nitro/helper/prototype.rb +49 -0
  35. data/lib/nitro/helper/scriptaculous.rb +0 -0
  36. data/lib/nitro/helper/xhtml.rb +11 -8
  37. data/lib/nitro/helper/xml.rb +1 -1
  38. data/lib/nitro/routing.rb +8 -1
  39. data/lib/nitro/scaffolding.rb +344 -0
  40. data/lib/nitro/server.rb +5 -1
  41. data/lib/nitro/server/runner.rb +19 -15
  42. data/lib/nitro/session.rb +32 -56
  43. data/lib/nitro/session/drbserver.rb +1 -1
  44. data/lib/nitro/session/file.rb +34 -15
  45. data/lib/nitro/session/memory.rb +13 -4
  46. data/lib/nitro/session/og.rb +56 -0
  47. data/proto/public/js/controls.js +30 -1
  48. data/proto/public/js/dragdrop.js +211 -146
  49. data/proto/public/js/effects.js +261 -399
  50. data/proto/public/js/prototype.js +131 -72
  51. data/proto/public/scaffold/edit.xhtml +10 -3
  52. data/proto/public/scaffold/form.xhtml +1 -7
  53. data/proto/public/scaffold/index.xhtml +20 -0
  54. data/proto/public/scaffold/list.xhtml +15 -8
  55. data/proto/public/scaffold/new.xhtml +10 -3
  56. data/proto/public/scaffold/search.xhtml +28 -0
  57. data/proto/public/scaffold/view.xhtml +8 -0
  58. data/proto/run.rb +93 -1
  59. data/src/part/admin.rb +4 -2
  60. data/src/part/admin/controller.rb +62 -28
  61. data/src/part/admin/skin.rb +8 -8
  62. data/src/part/admin/system.css +135 -0
  63. data/src/part/admin/template/index.xhtml +8 -12
  64. data/test/nitro/caching/tc_stores.rb +17 -0
  65. data/test/nitro/tc_caching.rb +1 -4
  66. data/test/nitro/tc_dispatcher.rb +22 -10
  67. data/test/nitro/tc_element.rb +1 -1
  68. data/test/nitro/tc_session.rb +23 -11
  69. data/test/public/blog/another/very_litle/index.xhtml +1 -0
  70. metadata +29 -15
  71. data/lib/nitro/dispatcher/general.rb +0 -62
  72. data/lib/nitro/dispatcher/nice.rb +0 -57
  73. data/lib/nitro/scaffold.rb +0 -171
  74. data/proto/public/index.xhtml +0 -83
  75. data/proto/public/js/scaffold.js +0 -74
  76. data/proto/public/settings.xhtml +0 -66
@@ -0,0 +1,274 @@
1
+ require 'nitro/helper/xhtml'
2
+
3
+ module Nitro
4
+
5
+ # :section: Property controls.
6
+
7
+ # A Form control.
8
+
9
+ class Control
10
+ include Nitro::XhtmlHelper
11
+
12
+ # Fetch the instance vars in a nice way use either rel
13
+ # or prop.
14
+ #
15
+ # values/value contain the contents of the prop or rel,
16
+ # (values reads better for relations)
17
+
18
+ attr_reader :prop
19
+ alias_method :rel, :prop
20
+
21
+ attr_reader :obj
22
+
23
+ attr_reader :value
24
+ alias_method :values, :value
25
+
26
+ # setup instance vars to use in methods
27
+
28
+ def initialize(obj, key, value=nil)
29
+ @obj = obj
30
+ @prop = key
31
+ @value = value || obj.send(key.name.to_sym)
32
+ end
33
+
34
+ # Main bulk of the control. Overide to customise
35
+
36
+ def render
37
+ "No view for this control"
38
+ end
39
+
40
+ # Label item. Override to customise
41
+
42
+ def label
43
+ %{<label for="#{prop.name}">#{prop[:title] || prop.name.to_s.humanize}</label>}
44
+ end
45
+
46
+ # Custom callback to process the request information
47
+ # posted back from the form
48
+ #
49
+ # When Property.populate_object (or fill) is called
50
+ # with the :preprocess => true option then this
51
+ # method will get called before the value is pushed
52
+ # onto the object that is getting 'filled'
53
+ #
54
+ # Overide this on controls that require special mods
55
+ # to the incoming values
56
+
57
+ def on_populate(val)
58
+ puts "PREPROCESSING !"
59
+ return val
60
+ end
61
+
62
+ private
63
+
64
+ def emit_style
65
+ if prop.respond_to?(:control_style)
66
+ style = prop.control_style
67
+ elsif self.class.respond_to?(:style)
68
+ style = self.class.style
69
+ else
70
+ style = nil
71
+ end
72
+ style ? %{ style="#{style}"} : ''
73
+ end
74
+
75
+ end
76
+
77
+ # Fixnum
78
+
79
+ class FixnumControl < Control
80
+ setting :style, :default => 'width: 100px', :doc => 'The default style'
81
+
82
+ def render
83
+ style = prop.control_style ||self.class.style
84
+ %{<input type="text" id="#{prop.symbol}_ctl" name="#{prop.symbol}" value="#{value}"#{emit_style} /> + -}
85
+ end
86
+ end
87
+
88
+ # Float
89
+
90
+ class FloatControl < Control
91
+ setting :style, :default => 'width: 100px', :doc => 'The default style'
92
+
93
+ def render
94
+ style = prop.control_style ||self.class.style
95
+ %{<input type="text" id="#{prop.symbol}_ctl" name="#{prop.symbol}" value="#{value}"#{emit_style} /> + -}
96
+ end
97
+ end
98
+
99
+ # Text
100
+
101
+ class TextControl < Control
102
+ setting :style, :default => 'width: 250px', :doc => 'The default style'
103
+
104
+ def render
105
+ %{<input type="text" id="#{prop.symbol}_ctl" name="#{prop.symbol}" value="#{value}"#{emit_style} />}
106
+ end
107
+ end
108
+
109
+ # Textarea
110
+
111
+ class TextareaControl < Control
112
+ setting :style, :default => 'width: 500px; height: 100px', :doc => 'The default style'
113
+
114
+ def render
115
+ %{<textarea id="#{prop.symbol}_ctl" name="#{prop.symbol}"#{emit_style}>#{value}</textarea>}
116
+ end
117
+ end
118
+
119
+ # CheckboxControl < Control
120
+
121
+ class CheckboxControl < Control
122
+ setting :style, :default => '', :doc => 'The default style'
123
+
124
+ def render
125
+ checked = value == true ? ' checked="checked"':''
126
+ %{<input type="checkbox" id="#{prop.symbol}_ctl" name="#{prop.symbol}" value="true"#{emit_style}#{checked} />}
127
+ end
128
+ end
129
+
130
+ # :section: Relation controls.
131
+
132
+ # RefersTo. Also used for BelongsTo.
133
+
134
+ class RefersToControl < Control
135
+ def render
136
+ objs = rel.target_class.all
137
+ if selected = value
138
+ selected = selected.pk
139
+ end
140
+
141
+ str = %{<select id="#{rel.name}_ctl" name="#{rel.name}">}
142
+ str << %{<option value="nil">None</option>}
143
+ str << %{#{options(:labels => objs.map{|o| o.to_s}, :values => objs.map{|o| o.pk}, :selected => selected)}</select>}
144
+
145
+ return str
146
+ end
147
+ end
148
+
149
+ # HasMany, ManyToMany and JoinsMany
150
+
151
+ class HasManyControl < Control
152
+
153
+ #pre :do_this, :on => :populate_object
154
+
155
+ def render
156
+ str = emit_container_start
157
+ str << emit_js
158
+ if selected_items.empty?
159
+ str << emit_selector(all_items, :removable => false)
160
+ else
161
+ removable = selected_items.size != 1 ? true : false
162
+ selected_items.each do |item|
163
+ str << emit_selector(all_items, :selected => item.pk)
164
+ end
165
+ end
166
+ str << emit_container_end
167
+ end
168
+
169
+ private
170
+
171
+ # these parts are seperated from render to make it easier
172
+ # to extend and customise the HasManyControl
173
+
174
+ def all_items
175
+ rel.target_class.all
176
+ end
177
+
178
+ def selected_items
179
+ values
180
+ end
181
+
182
+ def emit_container_start
183
+ %{<div class="many_to_many_container">}
184
+ end
185
+
186
+ def emit_container_end
187
+ %{</div>}
188
+ end
189
+
190
+ # :removable controls wether the minus button is active
191
+ # :selected denotes the oid to flag as selected in the list
192
+
193
+ def emit_selector(items, options={})
194
+ removable = options.fetch(:removable, true)
195
+ %{
196
+ <div>
197
+ <select class="has_many_ctl" name="#{rel.name}[]" #{emit_style}>
198
+ <option value="nil">None</option>
199
+ #{options(:labels => items.map{|o| o.to_s}, :values => items.map{|o| o.pk}, :selected => options[:selected])}
200
+ </select>
201
+ <input type="button" class="#{rel.name}_remove_btn" value=" - " onclick="rm_#{rel.name}_rel(this);" #{'disabled="disabled"' unless removable} />
202
+ <input type="button" class="#{rel.name}_add_btn" value=" + " onclick="add_#{rel.name}_rel(this);" />
203
+ </div>
204
+ }
205
+ end
206
+
207
+ # Inline script: override this to change behavior
208
+
209
+ def emit_js
210
+ %{
211
+ <script type="text/javascript">
212
+ rm_#{rel.name}_rel = function(el){
213
+ ctl=el.parentNode;
214
+ container=ctl.parentNode;
215
+ container.removeChild(ctl);
216
+ inputTags = container.getElementsByTagName('input');
217
+ if(inputTags.length==2)
218
+ inputTags[0].disabled='disabled';
219
+ }
220
+ add_#{rel.name}_rel = function(el){
221
+ ctl=el.parentNode;
222
+ container=ctl.parentNode;
223
+ node=ctl.cloneNode(true);
224
+ node.getElementsByTagName('input')[0].removeAttribute('disabled');
225
+ if(container.lastChild==ctl) container.appendChild(node);
226
+ else container.insertBefore(node, ctl.nextSibling);
227
+ if(container.childNodes.length>1) container.getElementsByTagName('input')[0].disabled='';
228
+ }
229
+ </script>
230
+ }
231
+ end
232
+ end
233
+
234
+ # The controls map.
235
+
236
+ class Control
237
+
238
+ # Setup the mapping of names => control classes
239
+
240
+ setting :map, :doc => 'Mappings of control names => classes', :default => {
241
+ :fixnum => FixnumControl,
242
+ :float => FloatControl,
243
+ :boolean => CheckboxControl,
244
+ :checkbox => CheckboxControl,
245
+ :string => TextControl,
246
+ :textarea => TextareaControl,
247
+ :true_class => CheckboxControl,
248
+ :refers_to => RefersToControl,
249
+ :belongs_to => RefersToControl,
250
+ :has_many => HasManyControl,
251
+ :many_to_many => HasManyControl,
252
+ :joins_many => HasManyControl
253
+ }
254
+
255
+ # Fetch a control, setup for 'obj' for a property, or relation
256
+ # or :symbol defaults to Control if not found
257
+
258
+ def self.fetch(obj, key, missing=self)
259
+ if key.kind_of? Og::Relation
260
+ control_sym = key[:control] || key.class.to_s.demodulize.underscore.to_sym
261
+ elsif key.kind_of? Property
262
+ control_sym = key[:control] || key.klass.to_s.underscore.to_sym
263
+ else
264
+ control_sym = key.to_sym
265
+ end
266
+ self.map.fetch(control_sym, missing).new(obj, key)
267
+ end
268
+
269
+ end
270
+
271
+ end
272
+
273
+ # * George Moschovitis <gm@navel.gr>
274
+ # * Chris Farmiloe <chris.farmiloe@farmiloe.com>
@@ -1,3 +1,7 @@
1
+ require 'nano/inflect'
2
+
3
+ require 'nitro/compiler/morphing'
4
+
1
5
  module Nitro
2
6
 
3
7
  # A collection of useful Javascript helpers. This modules
@@ -11,7 +15,15 @@ module Nitro
11
15
 
12
16
  module JavascriptHelper
13
17
 
14
- private
18
+ # Insert an anchor to execute a given function when the link is followed.
19
+ # Call with the name of the link, and the function to be called:
20
+ # link_to_function "Do it." :go
21
+
22
+ def link_to_function(name, function)
23
+ %{<a href="#" onclick="#{function}; return false;">#{name}</a>}
24
+ end
25
+
26
+ # -- older stuff --
15
27
 
16
28
  unless const_defined? :DEFAULT_JAVASCRIPT_FILES
17
29
  DEFAULT_JAVASCRIPT_FILES = [
@@ -24,7 +36,23 @@ private
24
36
  end
25
37
 
26
38
  # :section: behaviour.js
39
+ #
40
+ # Behaviour.js is a third-party library for keeping HTML clean of javascript.
41
+ # The +behaviour+ method provides an interface to that library.
42
+ # To learn more about the concept, visit the distributor:
43
+ # http://bennolan.com/behaviour/
27
44
 
45
+ # Register javascript code with an HTML element of a given id with the
46
+ # +behaviour+ method.
47
+ #
48
+ # Example:
49
+ #
50
+ # behaviour '#alert', %{
51
+ # el.onclick = function() {
52
+ # alert('Hello world');
53
+ # }
54
+ # }
55
+
28
56
  def behaviour(id, js)
29
57
  @_behaviours ||= []
30
58
  @_behaviours << [id, js]
@@ -32,6 +60,24 @@ private
32
60
  end
33
61
 
34
62
  # :section: prototype.js
63
+ #
64
+ # Prototype.js is a third-party library that provides a number of functions
65
+ # for AJAX-style interaction with the browser. It depends on the Behaviour.js
66
+ # library. Prototype's homepage is http://prototype.conio.net/
67
+
68
+ # A live, or asynchronous, request is one that does not bring the user to a
69
+ # new page. It is used to send data back to the web server while the user is
70
+ # still interacting with a document.
71
+ #
72
+ # Call +live+ with the id of an achor element as a string or a symbol.
73
+ # Alternatively, add <tt>async="true"</tt> to the anchor (A) element. Specify the
74
+ # anchor to be called either as a second parameter to the +live+ method, or
75
+ # in the HREF option of the anchor element.
76
+ #
77
+ # Examples:
78
+ #
79
+ # live :id_of_anchor_element [:method]
80
+ # <a href="receiver" async="true">Go!</a>
35
81
 
36
82
  def live_request(id, options = {})
37
83
  __append_script_file__ 'js/behaviour.js'
@@ -57,7 +103,8 @@ private
57
103
  alias_method :live, :live_request
58
104
  alias_method :async, :live_request
59
105
 
60
- # Denotes an element as toggleable.
106
+ # Clicking the element will make it disappear. If you want it to reappear,
107
+ # you'll have to call <tt>toggle()</tt>.
61
108
 
62
109
  def toggleable(id, options = {})
63
110
  __append_script_file__ 'js/prototype.js'
@@ -74,7 +121,7 @@ private
74
121
 
75
122
  # :section: script.aculo.us dragdrop.js
76
123
 
77
- # Make the element dragable.
124
+ # The user may click and drag the element about the screen.
78
125
 
79
126
  def draggable(id, options = {})
80
127
  __append_script_file__ 'js/behaviour.js'
@@ -215,7 +262,8 @@ private
215
262
  # -----------------------------------------------------------
216
263
  # :section: general javascript helpers.
217
264
 
218
- # Include external javascript file.
265
+ # Inserts links to the .js files necessary for your page. Call it from within
266
+ # HEAD. Add other script files as arguments if desired.
219
267
 
220
268
  def include_script(*files)
221
269
  return if @_script_files.nil? and files.empty?
@@ -253,13 +301,15 @@ private
253
301
  end
254
302
  alias_method :helper_css, :emit_css
255
303
 
256
- # Emits the aggregated helper javascript.
304
+ # Call this in your template/page to include the javascript statements that
305
+ # link your HTML to the javascript libraries. Must be called after the HTML
306
+ # elements involved, i.e., at the bottom of the page.
257
307
  #--
258
308
  # FIXME: refactor this!
259
309
  #++
260
310
 
261
311
  def emit_script
262
- code = %|<script type="text/javascript">\n|
312
+ code = %|<script type="text/javascript">\n<!--\n|
263
313
  unless @_behaviours.empty?
264
314
  code << %|var _behaviours = {\n|
265
315
  compo = []
@@ -276,6 +326,7 @@ private
276
326
  #{@_script.join("\n")}
277
327
  | if @_script
278
328
  code << %|
329
+ //-->
279
330
  </script>
280
331
  |
281
332
  end
@@ -307,6 +358,35 @@ private
307
358
 
308
359
  end
309
360
 
361
+ # :section: Javascript related morphers.
362
+
363
+ # Transform a normal achor into an asynchronous request:
364
+ # <a href="..." async="true">...</a>
365
+ # becomes
366
+ # <a href="#" onclick="new Ajax.Request...; return false;">...</a>
367
+
368
+ class AsyncMorpher < Morpher
369
+ def before_start(buffer)
370
+ href = @attributes['href']
371
+ @attributes['href'] = '#'
372
+ @attributes['onclick'] = "new Ajax.Request('#{href}'); return false;"
373
+ @attributes.delete(@key)
374
+ end
375
+ end
376
+
377
+ class LocalMorpher < Morpher
378
+ def before_start(buffer)
379
+ @attributes['href'] = '#'
380
+ @attributes['onclick'] = "ngs#{@value.camelcase(true)}();"
381
+ @attributes.delete(@key)
382
+ end
383
+ end
384
+
385
+ # Install the morphers.
386
+
387
+ Morphing.add_morpher :async, AsyncMorpher
388
+ Morphing.add_morpher :local, LocalMorpher
389
+
310
390
  end
311
391
 
312
392
  # * George Moschovitis <gm@navel.gr>
@@ -218,6 +218,11 @@ class Pager
218
218
  end
219
219
  alias_method :links, :navigation
220
220
 
221
+ def navigation_needed?
222
+ @page_count > 1
223
+ end
224
+ alias_method :navigation?, :navigation_needed?
225
+
221
226
  private
222
227
 
223
228
  # Generate the target URI.
@@ -0,0 +1,49 @@
1
+ module Nitro
2
+
3
+ module PrototypeHelper
4
+
5
+ def remote_function()
6
+ end
7
+
8
+ #--
9
+ # TODO: resolve html
10
+ #++
11
+
12
+ class JavascriptGenerator
13
+ attr_accessor :buffer
14
+
15
+ def initialize
16
+ @buffer = ''
17
+ end
18
+
19
+ # html = A string or a symbol to an action for rendering.
20
+ #--
21
+ # TODO: resolve html.
22
+ #++
23
+
24
+ def insert_html(id, html, options = {})
25
+ position = options.fetch(:where, :before)
26
+ record "new Insertion.#{position.to_s.camelize}(#{id.inspect}, #{html.inspect})"
27
+ end
28
+
29
+ def replace_html(id, html, options = {})
30
+ record "Element.update(#{id.inspect}, #{html.inspect})"
31
+ end
32
+
33
+ private
34
+
35
+ def record(code)
36
+ code = "#{line.to_s.chomp.gsub /\;$/, ''};"
37
+ @buffer << code
38
+ return code
39
+ end
40
+
41
+ end
42
+
43
+
44
+
45
+ end
46
+
47
+ end
48
+
49
+ # * George Moschovitis <gm@navel.gr>