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.
- data/bin/nitro +135 -37
- data/doc/CHANGELOG.1 +108 -108
- data/doc/CHANGELOG.2 +89 -89
- data/doc/CHANGELOG.3 +105 -105
- data/{CHANGELOG → doc/CHANGELOG.4} +509 -509
- data/doc/{AUTHORS → CONTRIBUTORS} +49 -37
- data/doc/LIBRARIES +13 -0
- data/doc/LICENSE +2 -3
- data/doc/MIGRATION +45 -0
- data/doc/RELEASES +131 -11
- data/doc/TODO +67 -0
- data/lib/glue/magick.rb +0 -3
- data/lib/glue/sweeper.rb +30 -15
- data/lib/glue/thumbnails.rb +0 -2
- data/lib/glue/webfile.rb +23 -11
- data/lib/nitro.rb +37 -44
- data/lib/nitro/adapter/cgi.rb +0 -3
- data/lib/nitro/adapter/console.rb +0 -2
- data/lib/nitro/adapter/fastcgi.rb +6 -3
- data/lib/nitro/adapter/mongrel.rb +97 -58
- data/lib/nitro/adapter/script.rb +4 -6
- data/lib/nitro/adapter/webrick.rb +33 -87
- data/lib/nitro/adapter/webrick/vcr.rb +85 -0
- data/lib/nitro/caching.rb +0 -2
- data/lib/nitro/caching/actions.rb +0 -2
- data/lib/nitro/caching/fragments.rb +0 -2
- data/lib/nitro/caching/output.rb +45 -16
- data/lib/nitro/caching/proxy.rb +49 -0
- data/lib/nitro/cgi.rb +3 -6
- data/lib/nitro/cgi/cookie.rb +0 -3
- data/lib/nitro/cgi/request.rb +67 -24
- data/lib/nitro/cgi/response.rb +0 -2
- data/lib/nitro/cgi/{sendfile.rb → send_file.rb} +7 -6
- data/lib/nitro/compiler.rb +62 -55
- data/lib/nitro/compiler/cleanup.rb +0 -3
- data/lib/nitro/compiler/elements.rb +31 -28
- data/lib/nitro/compiler/errors.rb +2 -5
- data/lib/nitro/compiler/include.rb +10 -8
- data/lib/nitro/compiler/layout.rb +0 -2
- data/lib/nitro/compiler/localization.rb +0 -2
- data/lib/nitro/compiler/markup.rb +14 -6
- data/lib/nitro/compiler/morphing.rb +1 -5
- data/lib/nitro/compiler/script.rb +2 -4
- data/lib/nitro/compiler/squeeze.rb +0 -2
- data/lib/nitro/compiler/xslt.rb +0 -2
- data/lib/nitro/context.rb +10 -5
- data/lib/nitro/control.rb +18 -0
- data/lib/nitro/control/attribute.rb +88 -0
- data/lib/nitro/control/attribute/checkbox.rb +19 -0
- data/lib/nitro/control/attribute/datetime.rb +21 -0
- data/lib/nitro/control/attribute/file.rb +20 -0
- data/lib/nitro/control/attribute/fixnum.rb +26 -0
- data/lib/nitro/control/attribute/float.rb +26 -0
- data/lib/nitro/control/attribute/options.rb +38 -0
- data/lib/nitro/control/attribute/password.rb +16 -0
- data/lib/nitro/control/attribute/text.rb +16 -0
- data/lib/nitro/control/attribute/textarea.rb +16 -0
- data/lib/nitro/control/none.rb +16 -0
- data/lib/nitro/control/relation.rb +53 -0
- data/lib/nitro/control/relation/belongs_to.rb +0 -0
- data/lib/nitro/control/relation/has_many.rb +97 -0
- data/lib/nitro/control/relation/joins_many.rb +0 -0
- data/lib/nitro/control/relation/many_to_many.rb +0 -0
- data/lib/nitro/control/relation/refers_to.rb +29 -0
- data/lib/nitro/controller.rb +7 -296
- data/lib/nitro/dispatcher.rb +72 -34
- data/lib/nitro/element.rb +36 -10
- data/lib/nitro/element/javascript.rb +0 -2
- data/lib/nitro/flash.rb +23 -10
- data/lib/nitro/global.rb +36 -11
- data/lib/nitro/helper.rb +22 -8
- data/lib/nitro/helper/benchmark.rb +0 -2
- data/lib/nitro/helper/buffer.rb +0 -3
- data/lib/nitro/helper/css.rb +12 -0
- data/lib/nitro/helper/debug.rb +1 -3
- data/lib/nitro/helper/default.rb +1 -0
- data/lib/nitro/helper/feed.rb +400 -386
- data/lib/nitro/helper/form.rb +246 -116
- data/lib/nitro/helper/javascript.rb +28 -2
- data/lib/nitro/helper/javascript/morphing.rb +0 -2
- data/lib/nitro/helper/javascript/prototype.rb +0 -2
- data/lib/nitro/helper/javascript/scriptaculous.rb +0 -1
- data/lib/nitro/helper/layout.rb +0 -2
- data/lib/nitro/helper/navigation.rb +87 -0
- data/lib/nitro/helper/pager.rb +11 -22
- data/lib/nitro/helper/table.rb +9 -32
- data/lib/nitro/helper/url.rb +104 -0
- data/lib/nitro/helper/xhtml.rb +20 -4
- data/lib/nitro/helper/xml.rb +0 -2
- data/lib/nitro/markup.rb +131 -0
- data/lib/nitro/part.rb +52 -7
- data/lib/nitro/publishable.rb +328 -0
- data/lib/nitro/render.rb +30 -61
- data/lib/nitro/router.rb +12 -4
- data/lib/nitro/sanitize.rb +48 -0
- data/lib/nitro/scaffold.rb +9 -11
- data/lib/nitro/scaffold/controller.rb +25 -0
- data/lib/nitro/scaffold/model.rb +150 -0
- data/lib/nitro/scaffolding.rb +1 -3
- data/lib/nitro/server.rb +57 -32
- data/lib/nitro/server/drb.rb +16 -2
- data/lib/nitro/server/runner.rb +80 -102
- data/lib/nitro/service.rb +0 -1
- data/lib/nitro/service/xmlrpc.rb +0 -2
- data/lib/nitro/session.rb +26 -18
- data/lib/nitro/session/drb.rb +2 -16
- data/lib/nitro/session/memory.rb +0 -2
- data/lib/nitro/template.rb +219 -0
- data/lib/nitro/test/assertions.rb +1 -3
- data/lib/nitro/test/context.rb +0 -1
- data/lib/nitro/test/testcase.rb +0 -1
- data/lib/nitro/version.rb +6 -0
- data/lib/part/admin.rb +16 -0
- data/lib/part/admin/controller.rb +19 -0
- data/lib/part/admin/helper.rb +30 -0
- data/lib/part/admin/og/controller.rb +114 -0
- data/lib/part/admin/og/customize.rb +4 -0
- data/lib/part/admin/og/template/index.xhtml +27 -0
- data/lib/part/admin/og/template/list.xhtml +38 -0
- data/lib/part/admin/og/template/search.xhtml +20 -0
- data/lib/part/admin/og/template/update.xhtml +25 -0
- data/lib/part/admin/skin.rb +207 -0
- data/lib/part/admin/template/denied.xhtml +13 -0
- data/lib/part/admin/template/index.xhtml +12 -0
- data/lib/part/admin/todo.txt +2 -0
- data/proto/public/error.xhtml +4 -2
- data/proto/run.rb +0 -2
- data/test/glue/tc_webfile.rb +1 -0
- data/test/nitro/cgi/tc_request.rb +23 -0
- data/test/nitro/helper/tc_feed.rb +0 -3
- data/test/nitro/helper/tc_navbar.rb +74 -0
- data/test/nitro/helper/tc_table.rb +2 -0
- data/test/nitro/tc_cgi.rb +72 -19
- data/test/nitro/tc_controller.rb +35 -26
- data/test/nitro/tc_controller_aspect.rb +1 -0
- data/test/nitro/tc_controller_params.rb +864 -0
- data/test/nitro/tc_dispatcher.rb +2 -2
- data/test/nitro/tc_element.rb +16 -16
- data/test/nitro/tc_flash.rb +3 -3
- data/test/nitro/tc_markup.rb +31 -0
- data/test/nitro/tc_render.rb +12 -14
- data/test/nitro/tc_session.rb +9 -7
- data/test/nitro/tc_template.rb +34 -0
- metadata +217 -198
- data/INSTALL +0 -121
- data/ProjectInfo +0 -74
- data/README +0 -555
- data/doc/apache.txt +0 -9
- data/doc/config.txt +0 -28
- data/doc/faq.txt +0 -7
- data/doc/lhttpd.txt +0 -7
- data/lib/nitro/adapter/scgi.rb +0 -239
- data/lib/nitro/helper/form/builder.rb +0 -144
- data/lib/nitro/helper/form/controls.rb +0 -389
- data/lib/nitro/helper/rss.rb +0 -72
- data/proto/conf/apache.conf +0 -51
- data/proto/public/scaffold/advanced_search.xhtml +0 -30
- data/proto/public/scaffold/edit.xhtml +0 -11
- data/proto/public/scaffold/form.xhtml +0 -1
- data/proto/public/scaffold/index.xhtml +0 -20
- data/proto/public/scaffold/list.xhtml +0 -32
- data/proto/public/scaffold/new.xhtml +0 -11
- data/proto/public/scaffold/search.xhtml +0 -29
- data/proto/public/scaffold/view.xhtml +0 -8
- data/proto/script/scgi_ctl +0 -221
- data/proto/script/scgi_service +0 -128
- data/setup.rb +0 -1585
- data/src/part/admin.rb +0 -16
- data/src/part/admin/controller.rb +0 -81
- data/src/part/admin/skin.rb +0 -21
- data/src/part/admin/system.css +0 -135
- data/src/part/admin/template/denied.xhtml +0 -1
- data/src/part/admin/template/index.xhtml +0 -43
- data/test/nitro/helper/tc_rss.rb +0 -24
data/doc/apache.txt
DELETED
data/doc/config.txt
DELETED
|
@@ -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
|
-
|
data/doc/faq.txt
DELETED
data/doc/lhttpd.txt
DELETED
data/lib/nitro/adapter/scgi.rb
DELETED
|
@@ -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>
|