nitro 0.26.0 → 0.27.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 (78) hide show
  1. data/CHANGELOG +312 -0
  2. data/INSTALL +3 -1
  3. data/ProjectInfo +6 -9
  4. data/README +32 -5
  5. data/Rakefile +5 -1
  6. data/bin/nitrogen +3 -60
  7. data/doc/MIGRATION +24 -0
  8. data/doc/RELEASES +141 -0
  9. data/doc/lhttpd.txt +3 -0
  10. data/lib/glue/magick.rb +38 -0
  11. data/lib/glue/thumbnails.rb +3 -0
  12. data/lib/glue/webfile.rb +137 -0
  13. data/lib/nitro.rb +1 -1
  14. data/lib/nitro/adapter/acgi.rb +235 -0
  15. data/lib/nitro/adapter/cgi.rb +16 -17
  16. data/lib/nitro/adapter/scgi.rb +4 -4
  17. data/lib/nitro/adapter/webrick.rb +9 -2
  18. data/lib/nitro/cgi.rb +49 -49
  19. data/lib/nitro/cgi/response.rb +4 -0
  20. data/lib/nitro/cgi/stream.rb +7 -7
  21. data/lib/nitro/cgi/utils.rb +2 -1
  22. data/lib/nitro/compiler.rb +47 -4
  23. data/lib/nitro/compiler/elements.rb +40 -20
  24. data/lib/nitro/compiler/layout.rb +21 -0
  25. data/lib/nitro/compiler/localization.rb +3 -1
  26. data/lib/nitro/compiler/markup.rb +2 -0
  27. data/lib/nitro/compiler/morphing.rb +16 -4
  28. data/lib/nitro/compiler/script.rb +109 -0
  29. data/lib/nitro/context.rb +10 -10
  30. data/lib/nitro/dispatcher.rb +4 -2
  31. data/lib/nitro/element.rb +107 -26
  32. data/lib/nitro/element/{java_script.rb → javascript.rb} +7 -1
  33. data/lib/nitro/flash.rb +4 -1
  34. data/lib/nitro/helper.rb +15 -0
  35. data/lib/nitro/helper/benchmark.rb +8 -2
  36. data/lib/nitro/helper/form.rb +3 -3
  37. data/lib/nitro/helper/form/controls.rb +131 -29
  38. data/lib/nitro/helper/{dojo.rb → form/test.xhtml} +0 -0
  39. data/lib/nitro/helper/javascript.rb +69 -59
  40. data/lib/nitro/helper/{scriptaculous.rb → javascript/dojo.rb} +0 -0
  41. data/lib/nitro/helper/javascript/morphing.rb +163 -0
  42. data/lib/nitro/helper/javascript/prototype.rb +96 -0
  43. data/lib/nitro/helper/javascript/scriptaculous.rb +18 -0
  44. data/lib/nitro/helper/layout.rb +42 -0
  45. data/lib/nitro/helper/table.rb +190 -27
  46. data/lib/nitro/{adapter → helper}/wee.rb +9 -3
  47. data/lib/nitro/render.rb +23 -17
  48. data/lib/nitro/scaffolding.rb +19 -2
  49. data/lib/nitro/server.rb +4 -8
  50. data/lib/nitro/server/runner.rb +28 -6
  51. data/lib/nitro/session.rb +7 -7
  52. data/lib/nitro_and_og.rb +2 -0
  53. data/proto/public/Makefile.acgi +40 -0
  54. data/proto/public/acgi.c +138 -0
  55. data/proto/public/js/builder.js +7 -3
  56. data/proto/public/js/controls.js +32 -12
  57. data/proto/public/js/dragdrop.js +4 -3
  58. data/proto/public/js/effects.js +111 -62
  59. data/proto/public/js/scriptaculous.js +10 -13
  60. data/proto/public/js/slider.js +88 -31
  61. data/proto/public/scaffold/new.xhtml +2 -2
  62. data/setup.rb +1585 -0
  63. data/src/part/admin.rb +6 -0
  64. data/src/part/admin/controller.rb +3 -3
  65. data/src/part/admin/skin.rb +1 -8
  66. data/test/nitro/adapter/tc_webrick.rb +2 -0
  67. data/test/nitro/tc_controller_aspect.rb +1 -1
  68. data/test/nitro/tc_element.rb +5 -6
  69. data/test/nitro/tc_table.rb +66 -0
  70. metadata +277 -271
  71. data/doc/architecture.txt +0 -2
  72. data/doc/bugs.txt +0 -15
  73. data/doc/tutorial.txt +0 -26
  74. data/install.rb +0 -37
  75. data/lib/nitro/compiler/script_generator.rb +0 -14
  76. data/lib/nitro/compiler/shaders.rb +0 -206
  77. data/lib/nitro/helper/prototype.rb +0 -49
  78. data/lib/nitro/scaffold/relations.rb +0 -54
data/lib/nitro/context.rb CHANGED
@@ -8,13 +8,13 @@ require 'nitro/session'
8
8
 
9
9
  module Nitro
10
10
 
11
- # Encapsulates an HTTP processing cycle context.
12
- # Integrates the HTTP Request, the HTTP Response
13
- # and the Render used to generate the response
14
- # body.
15
- # The Context object can be accessed by the
16
- # context, request or response aliases. You can
17
- # use the alias that makes sense every time.
11
+ # Encapsulates an HTTP processing cycle context. Integrates the
12
+ # HTTP Request, the HTTP Response and the Render used to
13
+ # generate the response body.
14
+ #
15
+ # The Context object can be accessed by the context, request or
16
+ # response aliases. You can use the alias that makes sense
17
+ # every time.
18
18
 
19
19
  class Context
20
20
  include Request
@@ -64,9 +64,9 @@ class Context
64
64
  #++
65
65
 
66
66
  def out
67
- return @out if @out == ""
67
+ return @out if @out == ''
68
68
  if @rendering_errors
69
- @out = String.new
69
+ @out = ''
70
70
  render '/error'
71
71
  end
72
72
  @out
@@ -88,7 +88,7 @@ class Context
88
88
  @dispatcher.controllers[@base || '/']
89
89
  end
90
90
 
91
- # Populate an object from request parameters.
91
+ # Automagically populate an object from request parameters.
92
92
  # This is a truly dangerous method.
93
93
  #
94
94
  # === Options
@@ -1,6 +1,7 @@
1
1
  require 'nano/kernel/singleton'
2
2
 
3
3
  require 'nitro/controller'
4
+ require 'nitro/compiler'
4
5
  require 'nitro/routing'
5
6
  require 'nitro/helper/default'
6
7
 
@@ -199,7 +200,7 @@ class Dispatcher
199
200
  #--
200
201
  # FIXME: no raise to make testable.
201
202
  #++
202
- raise NoActionError, "No action to dispatch to on #{klass}"
203
+ raise NoActionError, "No action for path '#{path}' on '#{klass}'"
203
204
  end
204
205
 
205
206
  # push any remaining parts of the url onto the query
@@ -223,7 +224,8 @@ private
223
224
  def controller_class_for(key)
224
225
  klass = @controllers[key]
225
226
 
226
- if klass and Compiler.reload
227
+ if $autoreload_dirty and klass and Compiler.reload
228
+ Logger.info "Reloading controller '#{klass}'"
227
229
  klass.instance_methods.grep(/(_action$)|(_template$)/).each do |m|
228
230
  klass.send(:remove_method, m) rescue nil
229
231
  end
data/lib/nitro/element.rb CHANGED
@@ -1,16 +1,34 @@
1
1
  require 'nano/string/capitalized'
2
2
  require 'nano/string/camelize'
3
+ require 'nano/string/underscore'
4
+ require 'nano/dir/self/recurse'
3
5
  require 'mega/annotation'
4
6
 
5
7
  require 'glue/flexob'
6
8
  require 'glue/configuration'
7
-
8
- # load the element compiler here for compatibility?
9
- #require 'nitro/compiler/elements'
9
+ require 'glue/template'
10
10
 
11
11
  module Nitro
12
12
 
13
- # A programmatically generated element.
13
+ # A programmatically generated element. Elements are a form
14
+ # of macros to allow for cleaner templates. They are evaluated
15
+ # at compile time, so there is no performance hit when you use
16
+ # them (at the expense of slightly reduced functionality).
17
+ #
18
+ # Nitro provides an additional method of defining elements.
19
+ # Instead of creating a lot of small classes, you can put
20
+ # .xhtml templates in the Element template_root. These templates
21
+ # are automatically converted into Element classes.
22
+ #
23
+ # For extra safety, you are advised to place your classes in the
24
+ # Nitro::Element namespace. If your classes do not extend
25
+ # Nitro::Element, the Nitro::ElementMixin is automatically
26
+ # injected in the class.
27
+ #
28
+ # An element can have and access a hierarchy of sub-elements.
29
+ # use #{content :sub_element_name} to access the render output
30
+ # of the subelement. Additionaly you can access the whole
31
+ # subelement object: _children[:sub_element_name]
14
32
  #
15
33
  # === Design
16
34
  #
@@ -21,11 +39,7 @@ module Nitro
21
39
  # * separate 'view' template files.
22
40
  #++
23
41
 
24
- class Element
25
- # The prefix for element tags (in xhtml compatibility mode)
26
-
27
- setting :prefix, :default => 'x', :doc => 'The prefix for element tags'
28
-
42
+ module ElementMixin
29
43
  # The parent of this element.
30
44
 
31
45
  attr_accessor :_parent
@@ -33,11 +47,8 @@ class Element
33
47
  # The children of this element.
34
48
 
35
49
  attr_accessor :_children
36
-
37
- # The named children of this element.
38
-
39
- attr_accessor :_named
40
-
50
+ alias_method :children, :_children
51
+
41
52
  # The text of this element.
42
53
 
43
54
  attr_accessor :_text
@@ -47,8 +58,7 @@ class Element
47
58
  attr_accessor :_view
48
59
 
49
60
  def initialize(*args)
50
- @_children = []
51
- @_named = Flexob.new
61
+ @_children = {}
52
62
  @_text = ''
53
63
  end
54
64
 
@@ -60,7 +70,7 @@ class Element
60
70
 
61
71
  def content(cname = nil)
62
72
  if cname
63
- @_named[cname].content
73
+ @_children[cname].content
64
74
  else
65
75
  @_text
66
76
  end
@@ -69,35 +79,106 @@ class Element
69
79
  def close
70
80
  end
71
81
 
82
+ # Renders the actual content of this Element.
83
+
72
84
  def render
73
85
  "#{open}#{content}#{close}"
74
86
  end
75
87
 
76
88
  def render_children
77
89
  str = ''
78
- for c in @_children
90
+ for c in @_children.values
79
91
  str << c.render
80
92
  end
81
93
 
82
94
  return str
83
95
  end
84
-
96
+
85
97
  def add_child(child)
86
98
  child._parent = self
87
- @_children << child
88
- if cname = child.class.ann.self[:name]
89
- @_named[cname] = child
90
- end
99
+ @_children[Element.class_to_name(child.class)] = child
91
100
  end
92
101
 
93
- alias_method :children, :_children
102
+ alias_method :children, :_children
103
+ end
104
+
105
+ # A programmatically generated element.
106
+ #
107
+ # === Design
108
+ #
109
+ # An underscore is used for the standard attibutes to
110
+ # avoid name clashes.
111
+ #--
112
+ # TODO:
113
+ # * separate 'view' template files.
114
+ #++
115
+
116
+ class Element
117
+ include ElementMixin
118
+
119
+ # The prefix for element tags (in xhtml compatibility mode)
120
+
121
+ setting :prefix, :default => 'x', :doc => 'The prefix for element tags'
122
+
123
+ # Allow auto extension of element classes?
124
+
125
+ setting :auto_extend, :default => true, :doc => 'Allow auto extension of element classes?'
126
+
127
+ # The directory where element templates reside. The default
128
+ # dir is #{Template.root}/element
129
+
130
+ if File.exist?(File.join(Template.root, 'element'))
131
+ default_root = File.join(Template.root, 'element')
132
+ else
133
+ default_root = 'element'
134
+ end
135
+
136
+ if File.exist?('src/element')
137
+ default_root = 'src/element'
138
+ elsif File.exist?('src/template/element')
139
+ default_root = 'src/template/element'
140
+ else
141
+ default_root = 'element'
142
+ end
94
143
 
144
+ setting :template_root, :default => default_root, :doc => 'The directory where element templates reside'
145
+
95
146
  class << self
96
- def name(sym)
97
- ann self, :name => sym.to_sym
147
+ # Return the name for an Element class.
148
+
149
+ def class_to_name(klass)
150
+ klass.to_s.underscore.to_sym
98
151
  end
152
+
153
+ # Compile the element templates into element classes.
154
+ # Typically called at startup.
155
+
156
+ def compile_template_elements
157
+ if File.exist? Element.template_root
158
+ Dir.recurse(Element.template_root) do |filename|
159
+ if filename =~ /\.#{Template.extension}$/
160
+ name = File.basename(filename).split('.').first.camelize
161
+ Nitro::Element.module_eval %{
162
+ class #{name} < Nitro::Element
163
+ def render
164
+ <<-END_OF_TEMPLATE
165
+ #{File.read(filename)}
166
+ END_OF_TEMPLATE
167
+ end
168
+ end
169
+ }
170
+ end
171
+ end
172
+ end
173
+ end
174
+
99
175
  end
100
176
  end
101
177
 
178
+ # Compile the element templates into element classes.
179
+
180
+ Element.compile_template_elements
181
+
102
182
  end
183
+
103
184
  # * George Moschovitis <gm@navel.gr>
@@ -1,6 +1,10 @@
1
1
  require 'nitro/element'
2
2
 
3
- class JavaScript < Nitro::Element
3
+ class Nitro::Element
4
+
5
+ # A javescript helper.
6
+
7
+ class Javascript
4
8
  def render
5
9
  %~
6
10
  <script type="text/javascript" language="javascript">
@@ -12,4 +16,6 @@ class JavaScript < Nitro::Element
12
16
  end
13
17
  end
14
18
 
19
+ end
20
+
15
21
  # * George Moschovitis <gm@navel.gr>
data/lib/nitro/flash.rb CHANGED
@@ -1,6 +1,8 @@
1
1
  module Nitro
2
2
 
3
+ #--
3
4
  # This module adds flashing support to the Controllers.
5
+ #++
4
6
 
5
7
  module Flashing
6
8
 
@@ -12,7 +14,8 @@ module Nitro
12
14
 
13
15
  # A Flash is a special hash object that lives in the session.
14
16
  # The values stored in the Flash are typically maintained
15
- # for the duration of one request.
17
+ # for the duration of one request. After the request is over,
18
+ # the Hash is cleared.
16
19
  #
17
20
  # You may want to use the Flash to pass error messages or
18
21
  # other short lived objects.
data/lib/nitro/helper.rb CHANGED
@@ -10,6 +10,8 @@ module Nitro
10
10
  # macro provided by Helpers, the utility methods are
11
11
  # included as private methods.
12
12
  #
13
+ # The helper also requires the ruby file.
14
+ #
13
15
  # === Examples
14
16
  #
15
17
  # class MyController < Nitro::Controller
@@ -36,6 +38,19 @@ module Helpers
36
38
  base.module_eval do
37
39
  def self.helper(*modules)
38
40
  for mod in modules
41
+ #--
42
+ # gmosx, FIXME: temp solution, will fix!
43
+ #++
44
+
45
+ if mod.is_a? String
46
+ begin
47
+ # gmosx: dont interpolate (RDoc fix).
48
+ require('src/helper/' + mod)
49
+ rescue Object
50
+ require('nitro/helper/' + mod) rescue nil
51
+ end
52
+ end
53
+
39
54
  unless mod.is_a? Module
40
55
  mod = mod.to_s.camelize
41
56
  # gmosx: check xxxHelper before xxx.
@@ -2,10 +2,16 @@ require 'benchmark'
2
2
 
3
3
  module Nitro
4
4
 
5
+ # This helper adds benchmarking support in your Controllers.
6
+ # Useful for fine tuning and optimizing your actions.
7
+
5
8
  module BenchmarkHelper
6
9
 
7
- # Log real time spent on a task. Example:
8
- # benchmark "Doing an operation" { operation }
10
+ # Log real time spent on a task.
11
+ #
12
+ # === Example
13
+ #
14
+ # benchmark "Doing an operation" { operation }
9
15
 
10
16
  def benchmark(message = 'Benchmarking')
11
17
  real = Benchmark.realtime { yield }
@@ -85,7 +85,7 @@ module FormHelper
85
85
  end
86
86
  end
87
87
  end
88
-
88
+
89
89
  action = "#{@base}/#{action}" unless action =~ /\//
90
90
 
91
91
  str << %{<form action="#{action}" method="post"#{enctype_attribute}>}
@@ -118,7 +118,7 @@ module FormHelper
118
118
  unless options[:all]
119
119
  next if prop.symbol == obj.class.primary_key.symbol or prop[:control] == :none or prop[:relation]
120
120
  end
121
- control = Control.fetch(obj,prop).render
121
+ control = Control.fetch(obj, prop, options).render
122
122
  str << FormBuilder.element(prop, control)
123
123
  end
124
124
  end
@@ -130,7 +130,7 @@ module FormHelper
130
130
  unless options[:all]
131
131
  next if rel[:control] == :none
132
132
  end
133
- control = Control.fetch(obj,rel).render
133
+ control = Control.fetch(obj, rel, options).render
134
134
  str << FormBuilder.element(rel, control)
135
135
  end
136
136
  end
@@ -25,10 +25,11 @@ class Control
25
25
 
26
26
  # setup instance vars to use in methods
27
27
 
28
- def initialize(obj, key, value=nil)
28
+ def initialize(obj, key, options = {})
29
29
  @obj = obj
30
30
  @prop = key
31
- @value = value || obj.send(key.name.to_sym)
31
+ @value = options[:value] || obj.send(key.name.to_sym)
32
+ @options = options
32
33
  end
33
34
 
34
35
  # Main bulk of the control. Overide to customise
@@ -55,7 +56,6 @@ class Control
55
56
  # to the incoming values
56
57
 
57
58
  def on_populate(val)
58
- puts "PREPROCESSING !"
59
59
  return val
60
60
  end
61
61
 
@@ -72,6 +72,18 @@ private
72
72
  style ? %{ style="#{style}"} : ''
73
73
  end
74
74
 
75
+ # add support to your controls for being disabled
76
+ # by including an emit_disabled on form items
77
+ # or testing for is_disabled? on more complex controls
78
+
79
+ def emit_disabled
80
+ is_disabled? ? %{ disabled="disabled"} : ''
81
+ end
82
+
83
+ def is_disabled?
84
+ return false if @options[:all]
85
+ @options[:disable_controls] || @prop.disable_control
86
+ end
75
87
  end
76
88
 
77
89
  # Fixnum
@@ -81,18 +93,32 @@ class FixnumControl < Control
81
93
 
82
94
  def render
83
95
  style = prop.control_style ||self.class.style
84
- %{<input type="text" id="#{prop.symbol}_ctl" name="#{prop.symbol}" value="#{value}"#{emit_style} /> + -}
96
+ %{
97
+ <div class="numeric_ctl_container">
98
+ <span class="numeric_ctl_input">
99
+ <input type="text" id="#{prop.symbol}_ctl" name="#{prop.symbol}" value="#{value}"#{emit_style}#{emit_disabled} />
100
+ </span>
101
+ <span class="numeric_ctl_buttons">
102
+ <a href="#" onclick="el=document.getElementById('#{prop.symbol}_ctl'); el.value=(parseInt(el.value) || 0)+#{step}; return false;">+</a>
103
+ <a href="#" onclick="el=document.getElementById('#{prop.symbol}_ctl'); el.value=(parseInt(el.value) || 0)-#{step}; return false;">-</a>
104
+ </span>
105
+ </div>
106
+ }
107
+ end
108
+
109
+ def step
110
+ 1
85
111
  end
112
+
86
113
  end
87
114
 
88
115
  # Float
89
116
 
90
- class FloatControl < Control
117
+ class FloatControl < FixnumControl
91
118
  setting :style, :default => 'width: 100px', :doc => 'The default style'
92
119
 
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} /> + -}
120
+ def step
121
+ 0.1
96
122
  end
97
123
  end
98
124
 
@@ -102,7 +128,17 @@ class TextControl < Control
102
128
  setting :style, :default => 'width: 250px', :doc => 'The default style'
103
129
 
104
130
  def render
105
- %{<input type="text" id="#{prop.symbol}_ctl" name="#{prop.symbol}" value="#{value}"#{emit_style} />}
131
+ %{<input type="text" id="#{prop.symbol}_ctl" name="#{prop.symbol}" value="#{value}"#{emit_style}#{emit_disabled} />}
132
+ end
133
+ end
134
+
135
+ # Password
136
+
137
+ class PasswordControl < Control
138
+ setting :style, :default => 'width: 250px', :doc => 'The default style'
139
+
140
+ def render
141
+ %{<input type="password" id="#{prop.symbol}_ctl" name="#{prop.symbol}" value="#{value}"#{emit_style} />}
106
142
  end
107
143
  end
108
144
 
@@ -112,7 +148,7 @@ class TextareaControl < Control
112
148
  setting :style, :default => 'width: 500px; height: 100px', :doc => 'The default style'
113
149
 
114
150
  def render
115
- %{<textarea id="#{prop.symbol}_ctl" name="#{prop.symbol}"#{emit_style}>#{value}</textarea>}
151
+ %{<textarea id="#{prop.symbol}_ctl" name="#{prop.symbol}"#{emit_style}#{emit_disabled}>#{value}</textarea>}
116
152
  end
117
153
  end
118
154
 
@@ -123,7 +159,61 @@ class CheckboxControl < Control
123
159
 
124
160
  def render
125
161
  checked = value == true ? ' checked="checked"':''
126
- %{<input type="checkbox" id="#{prop.symbol}_ctl" name="#{prop.symbol}" value="true"#{emit_style}#{checked} />}
162
+ %{<input type="checkbox" id="#{prop.symbol}_ctl" name="#{prop.symbol}" value="true"#{emit_style}#{checked}#{emit_disabled} />}
163
+ end
164
+ end
165
+
166
+ # ArrayControl
167
+
168
+ class ArrayControl < Control
169
+ def render
170
+ str = emit_container_start
171
+ str << emit_js
172
+ if values.empty?
173
+ str << emit_array_element(:removable => false)
174
+ else
175
+ removable = values.size != 1 ? true : false
176
+ values.each do |item|
177
+ str << emit_array_element(:selected => item.pk)
178
+ end
179
+ end
180
+ str << emit_container_end
181
+ end
182
+
183
+ def emit_array_element(options={})
184
+ removable = options.fetch(:removable, true)
185
+ selected = options.fetch(:selected, nil)
186
+ %{
187
+ <div>
188
+ <input type="text" id="#{prop.symbol}_ctl" name="#{prop.symbol}[]" value="#{value}"#{emit_style}#{emit_disabled} />
189
+ <input type="button" class="#{prop.symbol}_remove_btn" value=" - " onclick="rm_#{prop.symbol}_rel(this);" #{'disabled="disabled"' unless removable} />
190
+ <input type="button" class="#{prop.symbol}_add_btn" value=" + " onclick="add_#{prop.symbol}_rel(this);"#{emit_disabled} />
191
+ </div>
192
+ }
193
+ end
194
+
195
+ def emit_js
196
+ %{
197
+ <script type="text/javascript">
198
+ rm_#{prop.symbol}_rel = function(el){
199
+ ctl=el.parentNode;
200
+ container=ctl.parentNode;
201
+ container.removeChild(ctl);
202
+ inputTags = container.getElementsByTagName('input');
203
+ if(inputTags.length==2)
204
+ inputTags[0].disabled='disabled';
205
+ }
206
+ add_#{prop.symbol}_rel = function(el){
207
+ ctl=el.parentNode;
208
+ container=ctl.parentNode;
209
+ node=ctl.cloneNode(true);
210
+ node.getElementsByTagName('input')[0].removeAttribute('disabled');
211
+ if(container.lastChild==ctl) container.appendChild(node);
212
+ else container.insertBefore(node, ctl.nextSibling);
213
+ if(container.childNodes.length>1) container.getElementsByTagName('input')[0].disabled='';
214
+ }
215
+ </script>
216
+ }
127
217
  end
128
218
  end
129
219
 
@@ -133,16 +223,20 @@ end
133
223
 
134
224
  class RefersToControl < Control
135
225
  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
226
+ %{
227
+ <select id="#{rel.name}_ctl" name="#{rel.name}"#{emit_disabled}>
228
+ #{emit_options}
229
+ </select>
230
+ }
231
+ end
232
+
233
+ def emit_options
234
+ objs = rel.target_class.all
235
+ selected = selected.pk if selected = value
236
+ %{
237
+ <option value="nil">None</option>
238
+ #{options(:labels => objs.map{|o| o.to_s}, :values => objs.map{|o| o.pk}, :selected => selected)}
239
+ }
146
240
  end
147
241
  end
148
242
 
@@ -156,11 +250,11 @@ class HasManyControl < Control
156
250
  str = emit_container_start
157
251
  str << emit_js
158
252
  if selected_items.empty?
159
- str << emit_selector(all_items, :removable => false)
253
+ str << emit_selector(:removable => false)
160
254
  else
161
255
  removable = selected_items.size != 1 ? true : false
162
256
  selected_items.each do |item|
163
- str << emit_selector(all_items, :selected => item.pk)
257
+ str << emit_selector(:selected => item.pk)
164
258
  end
165
259
  end
166
260
  str << emit_container_end
@@ -190,16 +284,17 @@ class HasManyControl < Control
190
284
  # :removable controls wether the minus button is active
191
285
  # :selected denotes the oid to flag as selected in the list
192
286
 
193
- def emit_selector(items, options={})
287
+ def emit_selector(options={})
194
288
  removable = options.fetch(:removable, true)
289
+ selected = options.fetch(:selected, nil)
195
290
  %{
196
291
  <div>
197
- <select class="has_many_ctl" name="#{rel.name}[]" #{emit_style}>
292
+ <select class="has_many_ctl" name="#{rel.name}[]" #{emit_style}#{emit_disabled}>
198
293
  <option value="nil">None</option>
199
- #{options(:labels => items.map{|o| o.to_s}, :values => items.map{|o| o.pk}, :selected => options[:selected])}
294
+ #{options(:labels => all_items.map{|o| o.to_s}, :values => all_items.map{|o| o.pk}, :selected => selected)}
200
295
  </select>
201
296
  <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);" />
297
+ <input type="button" class="#{rel.name}_add_btn" value=" + " onclick="add_#{rel.name}_rel(this);"#{emit_disabled} />
203
298
  </div>
204
299
  }
205
300
  end
@@ -239,13 +334,17 @@ class Control
239
334
 
240
335
  setting :map, :doc => 'Mappings of control names => classes', :default => {
241
336
  :fixnum => FixnumControl,
337
+ :integer => FixnumControl,
242
338
  :float => FloatControl,
243
339
  :boolean => CheckboxControl,
244
340
  :checkbox => CheckboxControl,
245
341
  :string => TextControl,
342
+ :password => PasswordControl,
246
343
  :textarea => TextareaControl,
247
344
  :true_class => CheckboxControl,
345
+ :array => ArrayControl,
248
346
  :refers_to => RefersToControl,
347
+ :has_one => RefersToControl,
249
348
  :belongs_to => RefersToControl,
250
349
  :has_many => HasManyControl,
251
350
  :many_to_many => HasManyControl,
@@ -255,7 +354,8 @@ class Control
255
354
  # Fetch a control, setup for 'obj' for a property, or relation
256
355
  # or :symbol defaults to Control if not found
257
356
 
258
- def self.fetch(obj, key, missing=self)
357
+ def self.fetch(obj, key, options={})
358
+
259
359
  if key.kind_of? Og::Relation
260
360
  control_sym = key[:control] || key.class.to_s.demodulize.underscore.to_sym
261
361
  elsif key.kind_of? Property
@@ -263,7 +363,9 @@ class Control
263
363
  else
264
364
  control_sym = key.to_sym
265
365
  end
266
- self.map.fetch(control_sym, missing).new(obj, key)
366
+
367
+ default_to = options.fetch(:default, Control)
368
+ self.map.fetch(control_sym, Control).new(obj, key, options)
267
369
  end
268
370
 
269
371
  end