nitro 0.31.0 → 0.40.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 (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>