nitro 0.31.0 → 0.40.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (174) hide show
  1. data/bin/nitro +135 -37
  2. data/doc/CHANGELOG.1 +108 -108
  3. data/doc/CHANGELOG.2 +89 -89
  4. data/doc/CHANGELOG.3 +105 -105
  5. data/{CHANGELOG → doc/CHANGELOG.4} +509 -509
  6. data/doc/{AUTHORS → CONTRIBUTORS} +49 -37
  7. data/doc/LIBRARIES +13 -0
  8. data/doc/LICENSE +2 -3
  9. data/doc/MIGRATION +45 -0
  10. data/doc/RELEASES +131 -11
  11. data/doc/TODO +67 -0
  12. data/lib/glue/magick.rb +0 -3
  13. data/lib/glue/sweeper.rb +30 -15
  14. data/lib/glue/thumbnails.rb +0 -2
  15. data/lib/glue/webfile.rb +23 -11
  16. data/lib/nitro.rb +37 -44
  17. data/lib/nitro/adapter/cgi.rb +0 -3
  18. data/lib/nitro/adapter/console.rb +0 -2
  19. data/lib/nitro/adapter/fastcgi.rb +6 -3
  20. data/lib/nitro/adapter/mongrel.rb +97 -58
  21. data/lib/nitro/adapter/script.rb +4 -6
  22. data/lib/nitro/adapter/webrick.rb +33 -87
  23. data/lib/nitro/adapter/webrick/vcr.rb +85 -0
  24. data/lib/nitro/caching.rb +0 -2
  25. data/lib/nitro/caching/actions.rb +0 -2
  26. data/lib/nitro/caching/fragments.rb +0 -2
  27. data/lib/nitro/caching/output.rb +45 -16
  28. data/lib/nitro/caching/proxy.rb +49 -0
  29. data/lib/nitro/cgi.rb +3 -6
  30. data/lib/nitro/cgi/cookie.rb +0 -3
  31. data/lib/nitro/cgi/request.rb +67 -24
  32. data/lib/nitro/cgi/response.rb +0 -2
  33. data/lib/nitro/cgi/{sendfile.rb → send_file.rb} +7 -6
  34. data/lib/nitro/compiler.rb +62 -55
  35. data/lib/nitro/compiler/cleanup.rb +0 -3
  36. data/lib/nitro/compiler/elements.rb +31 -28
  37. data/lib/nitro/compiler/errors.rb +2 -5
  38. data/lib/nitro/compiler/include.rb +10 -8
  39. data/lib/nitro/compiler/layout.rb +0 -2
  40. data/lib/nitro/compiler/localization.rb +0 -2
  41. data/lib/nitro/compiler/markup.rb +14 -6
  42. data/lib/nitro/compiler/morphing.rb +1 -5
  43. data/lib/nitro/compiler/script.rb +2 -4
  44. data/lib/nitro/compiler/squeeze.rb +0 -2
  45. data/lib/nitro/compiler/xslt.rb +0 -2
  46. data/lib/nitro/context.rb +10 -5
  47. data/lib/nitro/control.rb +18 -0
  48. data/lib/nitro/control/attribute.rb +88 -0
  49. data/lib/nitro/control/attribute/checkbox.rb +19 -0
  50. data/lib/nitro/control/attribute/datetime.rb +21 -0
  51. data/lib/nitro/control/attribute/file.rb +20 -0
  52. data/lib/nitro/control/attribute/fixnum.rb +26 -0
  53. data/lib/nitro/control/attribute/float.rb +26 -0
  54. data/lib/nitro/control/attribute/options.rb +38 -0
  55. data/lib/nitro/control/attribute/password.rb +16 -0
  56. data/lib/nitro/control/attribute/text.rb +16 -0
  57. data/lib/nitro/control/attribute/textarea.rb +16 -0
  58. data/lib/nitro/control/none.rb +16 -0
  59. data/lib/nitro/control/relation.rb +53 -0
  60. data/lib/nitro/control/relation/belongs_to.rb +0 -0
  61. data/lib/nitro/control/relation/has_many.rb +97 -0
  62. data/lib/nitro/control/relation/joins_many.rb +0 -0
  63. data/lib/nitro/control/relation/many_to_many.rb +0 -0
  64. data/lib/nitro/control/relation/refers_to.rb +29 -0
  65. data/lib/nitro/controller.rb +7 -296
  66. data/lib/nitro/dispatcher.rb +72 -34
  67. data/lib/nitro/element.rb +36 -10
  68. data/lib/nitro/element/javascript.rb +0 -2
  69. data/lib/nitro/flash.rb +23 -10
  70. data/lib/nitro/global.rb +36 -11
  71. data/lib/nitro/helper.rb +22 -8
  72. data/lib/nitro/helper/benchmark.rb +0 -2
  73. data/lib/nitro/helper/buffer.rb +0 -3
  74. data/lib/nitro/helper/css.rb +12 -0
  75. data/lib/nitro/helper/debug.rb +1 -3
  76. data/lib/nitro/helper/default.rb +1 -0
  77. data/lib/nitro/helper/feed.rb +400 -386
  78. data/lib/nitro/helper/form.rb +246 -116
  79. data/lib/nitro/helper/javascript.rb +28 -2
  80. data/lib/nitro/helper/javascript/morphing.rb +0 -2
  81. data/lib/nitro/helper/javascript/prototype.rb +0 -2
  82. data/lib/nitro/helper/javascript/scriptaculous.rb +0 -1
  83. data/lib/nitro/helper/layout.rb +0 -2
  84. data/lib/nitro/helper/navigation.rb +87 -0
  85. data/lib/nitro/helper/pager.rb +11 -22
  86. data/lib/nitro/helper/table.rb +9 -32
  87. data/lib/nitro/helper/url.rb +104 -0
  88. data/lib/nitro/helper/xhtml.rb +20 -4
  89. data/lib/nitro/helper/xml.rb +0 -2
  90. data/lib/nitro/markup.rb +131 -0
  91. data/lib/nitro/part.rb +52 -7
  92. data/lib/nitro/publishable.rb +328 -0
  93. data/lib/nitro/render.rb +30 -61
  94. data/lib/nitro/router.rb +12 -4
  95. data/lib/nitro/sanitize.rb +48 -0
  96. data/lib/nitro/scaffold.rb +9 -11
  97. data/lib/nitro/scaffold/controller.rb +25 -0
  98. data/lib/nitro/scaffold/model.rb +150 -0
  99. data/lib/nitro/scaffolding.rb +1 -3
  100. data/lib/nitro/server.rb +57 -32
  101. data/lib/nitro/server/drb.rb +16 -2
  102. data/lib/nitro/server/runner.rb +80 -102
  103. data/lib/nitro/service.rb +0 -1
  104. data/lib/nitro/service/xmlrpc.rb +0 -2
  105. data/lib/nitro/session.rb +26 -18
  106. data/lib/nitro/session/drb.rb +2 -16
  107. data/lib/nitro/session/memory.rb +0 -2
  108. data/lib/nitro/template.rb +219 -0
  109. data/lib/nitro/test/assertions.rb +1 -3
  110. data/lib/nitro/test/context.rb +0 -1
  111. data/lib/nitro/test/testcase.rb +0 -1
  112. data/lib/nitro/version.rb +6 -0
  113. data/lib/part/admin.rb +16 -0
  114. data/lib/part/admin/controller.rb +19 -0
  115. data/lib/part/admin/helper.rb +30 -0
  116. data/lib/part/admin/og/controller.rb +114 -0
  117. data/lib/part/admin/og/customize.rb +4 -0
  118. data/lib/part/admin/og/template/index.xhtml +27 -0
  119. data/lib/part/admin/og/template/list.xhtml +38 -0
  120. data/lib/part/admin/og/template/search.xhtml +20 -0
  121. data/lib/part/admin/og/template/update.xhtml +25 -0
  122. data/lib/part/admin/skin.rb +207 -0
  123. data/lib/part/admin/template/denied.xhtml +13 -0
  124. data/lib/part/admin/template/index.xhtml +12 -0
  125. data/lib/part/admin/todo.txt +2 -0
  126. data/proto/public/error.xhtml +4 -2
  127. data/proto/run.rb +0 -2
  128. data/test/glue/tc_webfile.rb +1 -0
  129. data/test/nitro/cgi/tc_request.rb +23 -0
  130. data/test/nitro/helper/tc_feed.rb +0 -3
  131. data/test/nitro/helper/tc_navbar.rb +74 -0
  132. data/test/nitro/helper/tc_table.rb +2 -0
  133. data/test/nitro/tc_cgi.rb +72 -19
  134. data/test/nitro/tc_controller.rb +35 -26
  135. data/test/nitro/tc_controller_aspect.rb +1 -0
  136. data/test/nitro/tc_controller_params.rb +864 -0
  137. data/test/nitro/tc_dispatcher.rb +2 -2
  138. data/test/nitro/tc_element.rb +16 -16
  139. data/test/nitro/tc_flash.rb +3 -3
  140. data/test/nitro/tc_markup.rb +31 -0
  141. data/test/nitro/tc_render.rb +12 -14
  142. data/test/nitro/tc_session.rb +9 -7
  143. data/test/nitro/tc_template.rb +34 -0
  144. metadata +217 -198
  145. data/INSTALL +0 -121
  146. data/ProjectInfo +0 -74
  147. data/README +0 -555
  148. data/doc/apache.txt +0 -9
  149. data/doc/config.txt +0 -28
  150. data/doc/faq.txt +0 -7
  151. data/doc/lhttpd.txt +0 -7
  152. data/lib/nitro/adapter/scgi.rb +0 -239
  153. data/lib/nitro/helper/form/builder.rb +0 -144
  154. data/lib/nitro/helper/form/controls.rb +0 -389
  155. data/lib/nitro/helper/rss.rb +0 -72
  156. data/proto/conf/apache.conf +0 -51
  157. data/proto/public/scaffold/advanced_search.xhtml +0 -30
  158. data/proto/public/scaffold/edit.xhtml +0 -11
  159. data/proto/public/scaffold/form.xhtml +0 -1
  160. data/proto/public/scaffold/index.xhtml +0 -20
  161. data/proto/public/scaffold/list.xhtml +0 -32
  162. data/proto/public/scaffold/new.xhtml +0 -11
  163. data/proto/public/scaffold/search.xhtml +0 -29
  164. data/proto/public/scaffold/view.xhtml +0 -8
  165. data/proto/script/scgi_ctl +0 -221
  166. data/proto/script/scgi_service +0 -128
  167. data/setup.rb +0 -1585
  168. data/src/part/admin.rb +0 -16
  169. data/src/part/admin/controller.rb +0 -81
  170. data/src/part/admin/skin.rb +0 -21
  171. data/src/part/admin/system.css +0 -135
  172. data/src/part/admin/template/denied.xhtml +0 -1
  173. data/src/part/admin/template/index.xhtml +0 -43
  174. data/test/nitro/helper/tc_rss.rb +0 -24
@@ -1,9 +0,0 @@
1
- = Apache
2
-
3
- Nitro runs under the Apache Web Server using FastCGI.
4
-
5
- == Setup
6
-
7
- Download and setup the latest Apache distribution:
8
-
9
- Download and setup the latest mod_fcgi distirbution:
@@ -1,28 +0,0 @@
1
- = Configuration parameters
2
-
3
- This file presents a complete list of Nitro Configuration
4
- Parameters.
5
-
6
- INFO: To browse a documented listing of an application's settings
7
- point your browser to:
8
-
9
- http://www.myapp.com/settings.
10
-
11
-
12
- === Session.store_type = :memory
13
-
14
- Selects the mechanism employed for storing the sessions.
15
- Available values are:
16
-
17
- [+:memory+]
18
- This is the default mechanism, sessions are stored
19
- in memory. Only useful in multithreaded environments
20
- like WEBrick.
21
-
22
- [+:drb+]
23
- Distributed Sessions using DRb. An independed DRb
24
- server stores the sessions.
25
-
26
- In the future there will be more options available (:memcache,
27
- :filesys, :db)
28
-
@@ -1,7 +0,0 @@
1
- = Frequently Asked Questions
2
-
3
- === Who created Nitro ?
4
-
5
- Nitro was conceived and implemented by George Moschovitis
6
- (gm@navel.gr) with the help of numerous contributors. For
7
- more information, please consult doc/AUTHORS.
@@ -1,7 +0,0 @@
1
-
2
- = Installation
3
-
4
- ./configure --enable-rewrite
5
-
6
- The libpcre library is needed to enable mod_rewrite.
7
-
@@ -1,239 +0,0 @@
1
- #!/usr/bin/env ruby
2
-
3
- require 'stringio'
4
- require 'yaml'
5
- require 'digest/sha1'
6
- require 'socket'
7
- require 'cgi'
8
- require 'monitor'
9
- require 'singleton'
10
-
11
- module SCGI # :nodoc: all
12
-
13
- # Modifies CGI so that we can use it.
14
- class SCGIFixed < ::CGI # :nodoc: all
15
- public :env_table
16
-
17
- def initialize(params, data, out, *args)
18
- @env_table = params
19
- @args = *args
20
- @input = StringIO.new(data)
21
- @out = out
22
- super(*args)
23
- end
24
- def args
25
- @args
26
- end
27
- def env_table
28
- @env_table
29
- end
30
- alias_method :env, :env_table
31
-
32
- def stdinput
33
- @input
34
- end
35
- def stdoutput
36
- @out
37
- end
38
- end
39
-
40
- class SCGIProcessor < Monitor # :nodoc: all
41
- attr_reader :settings
42
-
43
- def initialize(server, settings = {})
44
- @conns = 0
45
- @shutdown = false
46
- @dead = false
47
- @server = server
48
- super()
49
-
50
- configure(settings)
51
- end
52
-
53
- def configure(settings)
54
- @settings = settings
55
- #@log = LogFactory.instance.create(settings[:logfile])
56
- @log = Logger
57
- @log = Logger.new(settings[:logfile]) if settings[:logfile]
58
-
59
- @maxconns = settings[:maxconns]
60
- @started = Time.now
61
-
62
- if settings[:socket]
63
- @socket = settings[:socket]
64
- else
65
- @host = settings[:host]
66
- @port = settings[:port]
67
- end
68
-
69
- @throttle_sleep = 1.0/settings[:conns_second] if settings[:conns_second]
70
- end
71
-
72
- def listen
73
- @socket = TCPServer.new(@host, @port)
74
-
75
- begin
76
- while true
77
- handle_client(@socket.accept)
78
- sleep @throttle_sleep if @throttle_sleep
79
- break if @shutdown and @conns <= 0
80
- end
81
- rescue Interrupt
82
- @log.info("SCGI: Shutting down from SIGINT.")
83
- rescue Object
84
- @log.warn("SCGI: while listening for connections on #@host:#@port -- #{$!.class} #$! #{$!.backtrace.join("\n")}" )
85
- end
86
-
87
- @socket.close if not @socket.closed?
88
- @dead = true
89
- @log.info("SCGI: Exited accept loop. Shutdown complete.")
90
- end
91
-
92
-
93
- def handle_client(socket)
94
- Thread.new do
95
- begin
96
- # remember if we were doing a shutdown so we can avoid increment later
97
- in_shutdown = @shutdown
98
- synchronize { @conns += 1 if not in_shutdown }
99
-
100
- len = ""
101
- # we only read 10 bytes of the length. any request longer than this is invalid
102
- while len.length <= 10
103
- c = socket.read(1)
104
- if c == ':'
105
- # found the terminal, len now has a length in it so read the payload
106
- break
107
- else
108
- len << c
109
- end
110
- end
111
-
112
- # we should now either have a payload length to get
113
- payload = socket.read(len.to_i)
114
- if (c = socket.read(1)) != ','
115
- @log.warn("SCGI: Malformed request, does not end with ','")
116
- else
117
- read_header(socket, payload, @conns)
118
- end
119
- rescue IOError
120
- @log.warn("SCGI: received IOError #$! when handling client. Your web server doesn't like me.")
121
- rescue Object
122
- @log.warn("SCGI: after accepting client #@host:#@port -- #{$!.class} #$! #{$!.backtrace.join("\n")}")
123
- ensure
124
- synchronize { @conns -= 1 if not in_shutdown}
125
- socket.close if not socket.closed?
126
- end
127
- end
128
- end
129
-
130
-
131
- def read_header(socket, payload, conns)
132
- return if socket.closed?
133
- request = split_body(payload)
134
- if request["CONTENT_LENGTH"]
135
- length = request["CONTENT_LENGTH"].to_i
136
- if length > 0
137
- body = socket.read(length)
138
- else
139
- body = ""
140
- end
141
-
142
- if @shutdown or @conns > @maxconns
143
- socket.write("Location: /busy.html\r\n")
144
- socket.write("Cache-control: no-cache, must-revalidate\r\n")
145
- socket.write("Expires: Mon, 26 Jul 1997 05:00:00 GMT\r\n")
146
- socket.write("Status: 307 Temporary Redirect\r\n\r\n")
147
- else
148
- process_request(request, body, socket)
149
- end
150
- end
151
- end
152
-
153
-
154
- def process_request(request, body, socket)
155
- return if socket.closed?
156
- cgi = SCGIFixed.new(request, body, socket)
157
- begin
158
- #--
159
- # TODO: remove sync, Nitro *is* thread safe!
160
- #++
161
- # guill: and why not ? ;)
162
- #synchronize do
163
- #--
164
- # FIXME: this is uggly, something better?
165
- #++
166
- cgi.stdinput.rewind
167
- cgi.env["QUERY_STRING"] = (cgi.env["REQUEST_URI"] =~ /^[^?]+\?(.+)$/ and $1).to_s
168
- Nitro::Cgi.process(@server, cgi, cgi.stdinput, cgi.stdoutput)
169
- #end
170
- ensure
171
- Og.manager.put_store if defined?(Og) and Og.respond_to?(:manager) and Og.manager
172
- end
173
- end
174
-
175
- def split_body(data)
176
- result = {}
177
- el = data.split("\0")
178
- i = 0
179
- len = el.length
180
- while i < len
181
- result[el[i]] = el[i+1]
182
- i += 2
183
- end
184
-
185
- return result
186
- end
187
-
188
-
189
- def status
190
- {
191
- :time => Time.now, :pid => Process.pid, :settings => @settings,
192
- :env => @settings[:env], :started => @started,
193
- :max_conns => @maxconns, :conns => @conns, :systimes => Process.times,
194
- :conns_second => @throttle, :shutdown => @shutdown, :dead => @dead
195
- }
196
- end
197
-
198
- # Graceful shutdown is done by setting the maxconns to 0 so that all new requests
199
- # get the 503 Service Unavailable status. The handler code checks if @shutdown is
200
- # set and if so it will not increase the @conns count, but it will decrease.
201
- # Once the @conns count is down to 0 it will exit the loop.
202
- def shutdown(force = false)
203
- @shutdown = true;
204
-
205
- if force
206
- @socket.close
207
- @log.info("SCGI: FORCED shutdown requested. Oh well.")
208
- else
209
- @log.info("SCGI: Shutdown requested. Beginning graceful shutdown with #@conns connected.")
210
- end
211
- end
212
- end
213
-
214
- class << self
215
- def start(server)
216
- settings = {
217
- :host => server.address,
218
- :port => server.port,
219
- :logfile => nil, # will use Logger
220
- :maxconns => 2**30-1,
221
- :socket => nil,
222
- :conns_second => nil,
223
- :env => nil,
224
- :drb_enable => false,
225
- :drb_port => server.port - 1000,
226
- :drb_password => ""
227
- }
228
-
229
- settings.update(server.options)
230
-
231
- @nitro = SCGIProcessor.new(server, settings)
232
- Logger.info("SCGI: Running on #{settings[:host]}:#{settings[:port]}")
233
- @nitro.listen
234
- end
235
- end
236
- end
237
-
238
- # * Zed A Shaw <zedshaw@zedshaw.com>
239
- # * George Moschovitis <gm@navel.gr>
@@ -1,144 +0,0 @@
1
- require 'glue/builder/xml'
2
-
3
- module Nitro
4
- module FormHelper
5
-
6
- # A specialized Builder for dynamically building of forms.
7
- # Provides extra support for forms backed by managed objects
8
- # (entities).
9
- #--
10
- # TODO: allow multiple objects per form.
11
- # TODO: use more generalized controls.
12
- #++
13
-
14
- class FormXmlBuilder < ::Glue::XmlBuilder
15
-
16
- def initialize buffer = '', options = {}
17
- super
18
- @obj = options[:object]
19
- end
20
-
21
- # Render a control+label for the given property of the form
22
- # object.
23
-
24
- def property sym, options = {}
25
- if prop = @obj.class.properties[sym]
26
- control = Form::Control.fetch(@obj, prop, options).render
27
- print element(prop, control)
28
- else
29
- raise "Undefined property '#{sym}' for class '#{@obj.class}'."
30
- end
31
- end
32
-
33
- # Render controls+labels for all relations of the form object.
34
-
35
- def all_properties options = {}
36
- for prop in @obj.class.properties.values
37
- property prop.symbol, options
38
- end
39
- end
40
- alias_method :properties, :all_properties
41
-
42
- #--
43
- # IMPLEMENT ME
44
- #++
45
-
46
- def relation sym, options = {}
47
- end
48
-
49
- #--
50
- # IMPLEMENT ME
51
- #++
52
-
53
- def all_relations options = {}
54
- end
55
-
56
- # Renders a control to select a file for upload.
57
-
58
- def select_file name, options = {}
59
- print %|<input type="file" name="#{name}" />|
60
- end
61
-
62
- private
63
-
64
- # Emit a label. Override this method to customize the
65
- # rendering for your application needs.
66
-
67
- def label prop
68
- %{<label for="#{prop.name}">#{prop[:title] || prop.name.to_s.humanize}</label>}
69
- end
70
-
71
- # Emit a form element. Override this method to customize the
72
- # rendering for your application needs.
73
-
74
- def element prop, html
75
- %{
76
- <p class="form_#{prop.symbol}">
77
- <div>#{label(prop)}</div>
78
- #{html}
79
- </p>
80
- }
81
- end
82
-
83
- end
84
-
85
- # A sophisticated form generation helper method.
86
- #
87
- # === Options
88
- #
89
- # * :object, :entity, :class = The object that acts as model
90
- # for this form. If you pass a class an empty object is
91
- # instantiated.
92
- #
93
- # * :action = The action of this form. The parameter is
94
- # passed through the R operator (encode_url) to support
95
- # advanced url encoding.
96
- #
97
- # === Example
98
- #
99
- # #{form(:object => @owner, :action => :save_profile) do |f|
100
- # f.property :name, :editable => false
101
- # f.property :password
102
- # f.br
103
- # f.submit 'Update'
104
- # end}
105
-
106
- def form options = {}, &block
107
- obj = (options[:object] ||= options[:entity] || options[:class])
108
-
109
- # If the passed obj is a Class instantiate an empty object
110
- # of this class.
111
-
112
- if obj.is_a? Class
113
- obj = options[:object] = obj.allocate
114
- end
115
-
116
- # Convert virtual :multipart method to method="post",
117
- # enctype="multipart/form-data"
118
-
119
- if options[:method] == :multipart
120
- options[:method] = :post
121
- options[:enctype] = 'multipart/form-data'
122
- end
123
-
124
- b = FormXmlBuilder.new('', options)
125
-
126
- b << '<form'
127
- b << %| action="#{R options[:action]}"| if options[:action]
128
- b << %| method="#{options[:method]}"| if options[:method]
129
- b << %| enctype="#{options[:enctype]}"| if options[:enctype]
130
- b << '>'
131
-
132
- b.hidden(:name => 'oid', :value => obj.oid) if obj and obj.saved?
133
-
134
- yield b
135
-
136
- b << '</form>'
137
-
138
- return b
139
- end
140
-
141
- end
142
- end
143
-
144
- # * George Moschovitis <gm@navel.gr>
@@ -1,389 +0,0 @@
1
- require 'glue/configuration'
2
-
3
- require 'nitro/helper/xhtml'
4
-
5
- module Nitro
6
-
7
- # :section: Property controls.
8
-
9
- module Form
10
-
11
- # A Form control.
12
-
13
- class Control
14
- include Nitro::XhtmlHelper
15
-
16
- # Fetch the instance vars in a nice way use either rel
17
- # or prop.
18
- #
19
- # values/value contain the contents of the prop or rel,
20
- # (values reads better for relations)
21
-
22
- attr_reader :prop
23
- alias_method :rel, :prop
24
-
25
- attr_reader :obj
26
-
27
- attr_reader :value
28
- alias_method :values, :value
29
-
30
- # setup instance vars to use in methods
31
-
32
- def initialize(obj, key, options = {})
33
- @obj = obj
34
- @prop = key
35
- @value = options[:value] || obj.send(key.name.to_sym)
36
- @options = options
37
- end
38
-
39
- # Main bulk of the control. Overide to customise
40
-
41
- def render
42
- "No view for this control"
43
- end
44
-
45
- # Label item. Override to customise
46
-
47
- def label
48
- %{<label for="#{prop.name}">#{prop[:title] || prop.name.to_s.humanize}</label>}
49
- end
50
-
51
- # Custom callback to process the request information
52
- # posted back from the form
53
- #
54
- # When Property.populate_object (or fill) is called
55
- # with the :preprocess => true option then this
56
- # method will get called before the value is pushed
57
- # onto the object that is getting 'filled'
58
- #
59
- # Overide this on controls that require special mods
60
- # to the incoming values
61
-
62
- def on_populate(val)
63
- return val
64
- end
65
-
66
- private
67
-
68
- def emit_style
69
- if prop.respond_to?(:control_style)
70
- style = prop.control_style
71
- elsif self.class.respond_to?(:style)
72
- style = self.class.style
73
- else
74
- style = nil
75
- end
76
- style ? %{ style="#{style}"} : ''
77
- end
78
-
79
- # add support to your controls for being disabled
80
- # by including an emit_disabled on form items
81
- # or testing for is_disabled? on more complex controls
82
-
83
- def emit_disabled
84
- is_disabled? ? %{ disabled="disabled"} : ''
85
- end
86
-
87
- def is_disabled?
88
- return false if @options[:all]
89
- @options[:disable_controls] || @prop.disable_control
90
- end
91
- end
92
-
93
- # Fixnum
94
-
95
- class FixnumControl < Control
96
- setting :style, :default => 'width: 100px', :doc => 'The default style'
97
-
98
- def render
99
- style = prop.control_style ||self.class.style
100
- %{
101
- <div class="numeric_ctl_container">
102
- <span class="numeric_ctl_input">
103
- <input type="text" id="#{prop.symbol}_ctl" name="#{prop.symbol}" value="#{value}"#{emit_style}#{emit_disabled} />
104
- </span>
105
- <span class="numeric_ctl_buttons">
106
- <a href="#" onclick="el=document.getElementById('#{prop.symbol}_ctl'); el.value=(parseInt(el.value) || 0)+#{step}; return false;">+</a>
107
- <a href="#" onclick="el=document.getElementById('#{prop.symbol}_ctl'); el.value=(parseInt(el.value) || 0)-#{step}; return false;">-</a>
108
- </span>
109
- </div>
110
- }
111
- end
112
-
113
- def step
114
- 1
115
- end
116
-
117
- end
118
-
119
- # Float
120
-
121
- class FloatControl < FixnumControl
122
- setting :style, :default => 'width: 100px', :doc => 'The default style'
123
-
124
- def step
125
- 0.1
126
- end
127
- end
128
-
129
- # Text
130
-
131
- class TextControl < Control
132
- setting :style, :default => 'width: 250px', :doc => 'The default style'
133
-
134
- def render
135
- %{<input type="text" id="#{prop.symbol}_ctl" name="#{prop.symbol}" value="#{value}"#{emit_style}#{emit_disabled} />}
136
- end
137
- end
138
-
139
- # Password
140
-
141
- class PasswordControl < Control
142
- setting :style, :default => 'width: 250px', :doc => 'The default style'
143
-
144
- def render
145
- %{<input type="password" id="#{prop.symbol}_ctl" name="#{prop.symbol}" value="#{value}"#{emit_style} />}
146
- end
147
- end
148
-
149
- # Textarea
150
-
151
- class TextareaControl < Control
152
- setting :style, :default => 'width: 500px; height: 100px', :doc => 'The default style'
153
-
154
- def render
155
- %{<textarea id="#{prop.symbol}_ctl" name="#{prop.symbol}"#{emit_style}#{emit_disabled}>#{value}</textarea>}
156
- end
157
- end
158
-
159
- # CheckboxControl < Control
160
-
161
- class CheckboxControl < Control
162
- setting :style, :default => '', :doc => 'The default style'
163
-
164
- def render
165
- checked = value == true ? ' checked="checked"':''
166
- %{<input type="checkbox" id="#{prop.symbol}_ctl" name="#{prop.symbol}" value="true"#{emit_style}#{checked}#{emit_disabled} />}
167
- end
168
- end
169
-
170
- # ArrayControl
171
-
172
- class ArrayControl < Control
173
- def render
174
- str = emit_container_start
175
- str << emit_js
176
- if values.empty?
177
- str << emit_array_element(:removable => false)
178
- else
179
- removable = values.size != 1 ? true : false
180
- values.each do |item|
181
- str << emit_array_element()
182
- end
183
- end
184
- str << emit_container_end
185
- end
186
-
187
- def emit_array_element(options={})
188
- removable = options.fetch(:removable, true)
189
- %{
190
- <div>
191
- <input type="text" id="#{prop.symbol}_ctl" name="#{prop.symbol}[]" value="#{value}"#{emit_style}#{emit_disabled} />
192
- <input type="button" class="#{prop.symbol}_remove_btn" value=" - " onclick="rm_#{prop.symbol}_rel(this);" #{'disabled="disabled"' unless removable} />
193
- <input type="button" class="#{prop.symbol}_add_btn" value=" + " onclick="add_#{prop.symbol}_rel(this);"#{emit_disabled} />
194
- </div>
195
- }
196
- end
197
-
198
- def emit_container_start
199
- %{<div class="array_container">}
200
- end
201
-
202
- def emit_container_end
203
- %{</div>}
204
- end
205
-
206
- def emit_js
207
- %{
208
- <script type="text/javascript">
209
- rm_#{prop.symbol}_rel = function(el){
210
- ctl=el.parentNode;
211
- container=ctl.parentNode;
212
- container.removeChild(ctl);
213
- inputTags = container.getElementsByTagName('input');
214
- if(inputTags.length==2)
215
- inputTags[0].disabled='disabled';
216
- }
217
- add_#{prop.symbol}_rel = function(el){
218
- ctl=el.parentNode;
219
- container=ctl.parentNode;
220
- node=ctl.cloneNode(true);
221
- node.getElementsByTagName('input')[0].removeAttribute('disabled');
222
- if(container.lastChild==ctl) container.appendChild(node);
223
- else container.insertBefore(node, ctl.nextSibling);
224
- if(container.childNodes.length>1) container.getElementsByTagName('input')[1].disabled='';
225
- }
226
- </script>
227
- }
228
- end
229
- end
230
-
231
- # :section: Relation controls.
232
-
233
- # RefersTo. Also used for BelongsTo.
234
-
235
- class RefersToControl < Control
236
- def render
237
- %{
238
- <select id="#{rel.name}_ctl" name="#{rel.name}"#{emit_disabled}>
239
- #{emit_options}
240
- </select>
241
- }
242
- end
243
-
244
- def emit_options
245
- objs = rel.target_class.all
246
- selected = selected.pk if selected = value
247
- %{
248
- <option value="nil">None</option>
249
- #{options(:labels => objs.map{|o| o.to_s}, :values => objs.map{|o| o.pk}, :selected => selected)}
250
- }
251
- end
252
- end
253
-
254
- # HasMany, ManyToMany and JoinsMany
255
-
256
- class HasManyControl < Control
257
-
258
- #pre :do_this, :on => :populate_object
259
-
260
- def render
261
- str = emit_container_start
262
- str << emit_js
263
- if selected_items.empty?
264
- str << emit_selector(:removable => false)
265
- else
266
- removable = selected_items.size != 1 ? true : false
267
- selected_items.each do |item|
268
- str << emit_selector(:selected => item.pk)
269
- end
270
- end
271
- str << emit_container_end
272
- end
273
-
274
- private
275
-
276
- # these parts are seperated from render to make it easier
277
- # to extend and customise the HasManyControl
278
-
279
- def all_items
280
- return @all_items unless @all_items.nil?
281
- @all_items = rel.target_class.all
282
- end
283
-
284
- def selected_items
285
- values
286
- end
287
-
288
- def emit_container_start
289
- %{<div class="many_to_many_container">}
290
- end
291
-
292
- def emit_container_end
293
- %{</div>}
294
- end
295
-
296
- # :removable controls wether the minus button is active
297
- # :selected denotes the oid to flag as selected in the list
298
-
299
- def emit_selector(options={})
300
- removable = options.fetch(:removable, true)
301
- selected = options.fetch(:selected, nil)
302
- %{
303
- <div>
304
- <select class="has_many_ctl" name="#{rel.name}[]" #{emit_style}#{emit_disabled}>
305
- <option value="nil">None</option>
306
- #{options(:labels => all_items.map{|o| o.to_s}, :values => all_items.map{|o| o.pk}, :selected => selected)}
307
- </select>
308
- <input type="button" class="#{rel.name}_remove_btn" value=" - " onclick="rm_#{rel.name}_rel(this);" #{'disabled="disabled"' unless removable} />
309
- <input type="button" class="#{rel.name}_add_btn" value=" + " onclick="add_#{rel.name}_rel(this);"#{emit_disabled} />
310
- </div>
311
- }
312
- end
313
-
314
- # Inline script: override this to change behavior
315
-
316
- def emit_js
317
- %{
318
- <script type="text/javascript">
319
- rm_#{rel.name}_rel = function(el){
320
- ctl=el.parentNode;
321
- container=ctl.parentNode;
322
- container.removeChild(ctl);
323
- inputTags = container.getElementsByTagName('input');
324
- if(inputTags.length==2)
325
- inputTags[0].disabled='disabled';
326
- }
327
- add_#{rel.name}_rel = function(el){
328
- ctl=el.parentNode;
329
- container=ctl.parentNode;
330
- node=ctl.cloneNode(true);
331
- node.getElementsByTagName('input')[0].removeAttribute('disabled');
332
- if(container.lastChild==ctl) container.appendChild(node);
333
- else container.insertBefore(node, ctl.nextSibling);
334
- if(container.childNodes.length>1) container.getElementsByTagName('input')[0].disabled='';
335
- }
336
- </script>
337
- }
338
- end
339
- end
340
-
341
- # The controls map.
342
-
343
- class Control
344
-
345
- # Setup the mapping of names => control classes
346
-
347
- setting :map, :doc => 'Mappings of control names => classes', :default => {
348
- :fixnum => FixnumControl,
349
- :integer => FixnumControl,
350
- :float => FloatControl,
351
- :boolean => CheckboxControl,
352
- :checkbox => CheckboxControl,
353
- :string => TextControl,
354
- :password => PasswordControl,
355
- :textarea => TextareaControl,
356
- :true_class => CheckboxControl,
357
- :array => ArrayControl,
358
- :refers_to => RefersToControl,
359
- :has_one => RefersToControl,
360
- :belongs_to => RefersToControl,
361
- :has_many => HasManyControl,
362
- :many_to_many => HasManyControl,
363
- :joins_many => HasManyControl
364
- }
365
-
366
- # Fetch a control, setup for 'obj' for a property, or relation
367
- # or :symbol defaults to Control if not found
368
-
369
- def self.fetch(obj, key, options={})
370
- if key.kind_of? Og::Relation
371
- control_sym = key[:control] || key.class.to_s.demodulize.underscore.to_sym
372
- elsif key.kind_of? Property
373
- control_sym = key[:control] || key.klass.to_s.underscore.to_sym
374
- else
375
- control_sym = key.to_sym
376
- end
377
-
378
- default_to = options.fetch(:default, Control)
379
- self.map.fetch(control_sym, Control).new(obj, key, options)
380
- end
381
-
382
- end
383
-
384
- end
385
-
386
- end
387
-
388
- # * George Moschovitis <gm@navel.gr>
389
- # * Chris Farmiloe <chris.farmiloe@farmiloe.com>