nitro 0.25.0 → 0.26.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 +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>