nitro 0.29.0 → 0.30.0
Sign up to get free protection for your applications and to get access to all the features.
- data/CHANGELOG +410 -0
- data/ProjectInfo +36 -44
- data/README +5 -5
- data/doc/AUTHORS +6 -0
- data/doc/RELEASES +159 -2
- data/lib/glue/sweeper.rb +2 -2
- data/lib/glue/webfile.rb +14 -1
- data/lib/nitro.rb +6 -9
- data/lib/nitro/adapter/mongrel.rb +36 -43
- data/lib/nitro/adapter/scgi.rb +1 -1
- data/lib/nitro/adapter/webrick.rb +96 -24
- data/lib/nitro/caching/actions.rb +2 -1
- data/lib/nitro/caching/fragments.rb +1 -8
- data/lib/nitro/caching/output.rb +14 -4
- data/lib/nitro/cgi.rb +19 -21
- data/lib/nitro/cgi/cookie.rb +5 -1
- data/lib/nitro/cgi/request.rb +20 -4
- data/lib/nitro/compiler.rb +74 -28
- data/lib/nitro/compiler/cleanup.rb +1 -1
- data/lib/nitro/compiler/elements.rb +1 -2
- data/lib/nitro/compiler/localization.rb +1 -1
- data/lib/nitro/compiler/markup.rb +1 -1
- data/lib/nitro/compiler/script.rb +52 -44
- data/lib/nitro/compiler/squeeze.rb +4 -3
- data/lib/nitro/compiler/xslt.rb +7 -6
- data/lib/nitro/context.rb +39 -20
- data/lib/nitro/controller.rb +24 -5
- data/lib/nitro/dispatcher.rb +13 -5
- data/lib/nitro/global.rb +63 -0
- data/lib/nitro/helper/feed.rb +432 -0
- data/lib/nitro/helper/form.rb +11 -3
- data/lib/nitro/helper/form/builder.rb +140 -0
- data/lib/nitro/helper/form/controls.rb +2 -1
- data/lib/nitro/helper/javascript.rb +6 -0
- data/lib/nitro/helper/javascript/morphing.rb +13 -6
- data/lib/nitro/helper/xhtml.rb +42 -6
- data/lib/nitro/helper/xml.rb +3 -0
- data/lib/nitro/part.rb +2 -2
- data/lib/nitro/render.rb +7 -2
- data/lib/nitro/router.rb +57 -16
- data/lib/nitro/scaffolding.rb +29 -20
- data/lib/nitro/server.rb +4 -10
- data/lib/nitro/server/drb.rb +1 -1
- data/lib/nitro/server/runner.rb +10 -0
- data/lib/nitro/session.rb +31 -12
- data/lib/nitro/session/drb.rb +13 -1
- data/lib/nitro/session/file.rb +1 -1
- data/lib/nitro/session/memcached.rb +1 -1
- data/lib/nitro/session/memory.rb +1 -1
- data/lib/nitro/session/og.rb +1 -1
- data/lib/nitro/test/testcase.rb +3 -0
- data/proto/public/error.xhtml +5 -5
- data/proto/public/js/controls.js +2 -2
- data/proto/public/js/dragdrop.js +320 -79
- data/proto/public/js/effects.js +200 -152
- data/proto/public/js/prototype.js +284 -63
- data/proto/public/js/scriptaculous.js +7 -5
- data/proto/public/js/unittest.js +11 -0
- data/proto/public/scaffold/advanced_search.xhtml +30 -0
- data/proto/public/scaffold/list.xhtml +8 -1
- data/proto/public/scaffold/search.xhtml +2 -1
- data/proto/script/scgi_service +1 -1
- data/src/part/admin/controller.rb +1 -1
- data/src/part/admin/skin.rb +1 -1
- data/test/nitro/CONFIG.rb +3 -0
- data/test/nitro/adapter/tc_webrick.rb +1 -1
- data/test/nitro/cgi/tc_cookie.rb +1 -1
- data/test/nitro/cgi/tc_request.rb +5 -5
- data/test/nitro/compiler/tc_client_morpher.rb +47 -0
- data/test/nitro/compiler/tc_compiler.rb +2 -0
- data/test/nitro/helper/tc_feed.rb +138 -0
- data/test/nitro/helper/tc_pager.rb +1 -1
- data/test/nitro/helper/tc_rss.rb +1 -1
- data/test/nitro/helper/tc_table.rb +1 -1
- data/test/nitro/helper/tc_xhtml.rb +1 -1
- data/test/nitro/tc_caching.rb +1 -1
- data/test/nitro/tc_cgi.rb +1 -1
- data/test/nitro/tc_context.rb +1 -1
- data/test/nitro/tc_controller.rb +31 -3
- data/test/nitro/tc_controller_aspect.rb +1 -1
- data/test/nitro/tc_dispatcher.rb +1 -1
- data/test/nitro/tc_element.rb +1 -1
- data/test/nitro/tc_flash.rb +1 -1
- data/test/nitro/tc_helper.rb +1 -1
- data/test/nitro/tc_render.rb +6 -6
- data/test/nitro/tc_router.rb +8 -4
- data/test/nitro/tc_server.rb +1 -3
- data/test/nitro/tc_session.rb +1 -3
- metadata +107 -104
- data/Rakefile +0 -232
- data/lib/nitro/adapter/acgi.rb +0 -237
- data/proto/public/Makefile.acgi +0 -40
- data/proto/public/acgi.c +0 -138
@@ -0,0 +1,432 @@
|
|
1
|
+
require 'rss/maker'
|
2
|
+
require 'glue/markup'
|
3
|
+
require 'rexml/document'
|
4
|
+
require 'time'
|
5
|
+
require 'uri'
|
6
|
+
|
7
|
+
require 'facet/string/first_char'
|
8
|
+
|
9
|
+
module Nitro
|
10
|
+
|
11
|
+
# A helper that provides Feed related methods.
|
12
|
+
#
|
13
|
+
# To include this helper into your controller,
|
14
|
+
# add the following at the beginning of your controller:
|
15
|
+
#
|
16
|
+
# helper :feed
|
17
|
+
#
|
18
|
+
# Then define actions that set an appropriate content_type and use build_(rss|atom|opml)
|
19
|
+
# to generate your desired feed. See below for details.
|
20
|
+
#
|
21
|
+
# == RSS 0.91, 1.0 and 2.0
|
22
|
+
#
|
23
|
+
# response.content_type = "application/rss+xml"
|
24
|
+
# build_rss(og_objects,
|
25
|
+
# :version => "0.9",
|
26
|
+
# :base => context.host_url, # + object.to_href results in an item-link
|
27
|
+
# :link => context.host_url+"/feed", # link to this feed
|
28
|
+
# :title => "Feed Title",
|
29
|
+
# :description => "What this feed is about",
|
30
|
+
# :search_title => "Search Form Here",
|
31
|
+
# :search_description => "Search description",
|
32
|
+
# :search_input_name => "search_field",
|
33
|
+
# :search_form_action => "http://url/to/search_action"
|
34
|
+
# )
|
35
|
+
#
|
36
|
+
# For RSS 1.0 or RSS 2.0 just change :version (defaults to '0.91'),
|
37
|
+
# possible :version options are "0.9", "0.91", "1.0" and "2.0"
|
38
|
+
#
|
39
|
+
# * for RSS 0.9 :language is required (or defaults to 'en')
|
40
|
+
# * for all RSS versions :title, :link and/or :base, :description are required
|
41
|
+
#
|
42
|
+
# <b>individual objects have to respond to at least:</b>
|
43
|
+
#
|
44
|
+
# * 1.0/0.9/2.0 require @title
|
45
|
+
# * 1.0/0.9 require @to_href
|
46
|
+
# * 2.0 requires @body
|
47
|
+
#
|
48
|
+
# if it doesn't, no item is created
|
49
|
+
#
|
50
|
+
# * @update_time, @create_time or @date is used for item.date
|
51
|
+
# * so if Og's "is Timestamped" is being used, it'll be @update_time
|
52
|
+
# * @author[:name] can optionally be used for item.author
|
53
|
+
#
|
54
|
+
# == Atom 1.0
|
55
|
+
#
|
56
|
+
# response.content_type = "application/atom+xml"
|
57
|
+
# build_atom(og_objects,
|
58
|
+
# :title => "Feed Title",
|
59
|
+
# :base => context.host_url, # + object.to_href results in an item-link
|
60
|
+
# :link => context.host_url+"/atomfeed",
|
61
|
+
# :id => "your_unique_id", # :base is being used unless :id specified (:base is recommended)
|
62
|
+
# :author_name => "Takeo",
|
63
|
+
# :author_email => "email@example.com",
|
64
|
+
# :author_link => "http://uri.to/authors/home",
|
65
|
+
# )
|
66
|
+
#
|
67
|
+
# <b>individual objects have to respond to at least:</b>
|
68
|
+
#
|
69
|
+
# * @title
|
70
|
+
# * @to_href
|
71
|
+
# * @update_time/@create_time/@date (at least one of them)
|
72
|
+
#
|
73
|
+
# if it doesn't, no entry is created
|
74
|
+
#
|
75
|
+
# optional:
|
76
|
+
#
|
77
|
+
# * @body (taken as summary (256 chars))
|
78
|
+
# * @full_content
|
79
|
+
# * use Og's "is Timestamped", so both @update_time and @create_time can be used
|
80
|
+
# * @author[:name]
|
81
|
+
# * @author[:link]
|
82
|
+
# * @author[:email] # be careful, you don't want to publish your users email address to spammers
|
83
|
+
#
|
84
|
+
#
|
85
|
+
# == OPML 1.0 feed lists
|
86
|
+
#
|
87
|
+
# Fabian: Eew, who invented OPML? Who needs it? Implementing it in a very rough way anyway though.
|
88
|
+
# takes a Hash of Feeds and optional options
|
89
|
+
#
|
90
|
+
# response.content_type = "application/opml+xml"
|
91
|
+
# build_opml(
|
92
|
+
# {
|
93
|
+
# "http://oxyliquit.de/feed" => "rss",
|
94
|
+
# "http://oxyliquit.de/feed/questions" => "rss",
|
95
|
+
# "http://oxyliquit.de/feed/tips" => "rss",
|
96
|
+
# "http://oxyliquit.de/feed/tutorials" => "rss"
|
97
|
+
# },
|
98
|
+
# :title => "My feeds"
|
99
|
+
# )
|
100
|
+
|
101
|
+
module FeedHelper
|
102
|
+
include Glue::Markup
|
103
|
+
|
104
|
+
# RSS 0.91, 1.0, 2.0 feeds.
|
105
|
+
|
106
|
+
def build_rss(objects, options = {})
|
107
|
+
|
108
|
+
# default options
|
109
|
+
options = {
|
110
|
+
:title => 'Syndication',
|
111
|
+
:description => 'Syndication',
|
112
|
+
:version => '0.9',
|
113
|
+
:language => 'en', # required by 0.9
|
114
|
+
}.update(options)
|
115
|
+
|
116
|
+
raise "Option ':version' contains a wrong version!" unless %w(0.9 0.91 1.0 2.0).include?(options[:version])
|
117
|
+
|
118
|
+
options[:base] ||= options[:link]
|
119
|
+
raise "Option ':base' cannot be omitted!" unless options[:base]
|
120
|
+
|
121
|
+
# build rss
|
122
|
+
rss = RSS::Maker.make(options[:version]) do |maker|
|
123
|
+
maker.channel.title = options[:title]
|
124
|
+
maker.channel.description = options[:description]
|
125
|
+
if options[:link]
|
126
|
+
maker.channel.link = options[:link]
|
127
|
+
else
|
128
|
+
maker.channel.link = options[:base] #FIXME: not sure
|
129
|
+
end
|
130
|
+
case options[:version]
|
131
|
+
when '0.9', '0.91'
|
132
|
+
maker.channel.language = options[:language]
|
133
|
+
when '1.0'
|
134
|
+
if options[:link]
|
135
|
+
maker.channel.about = options[:link]
|
136
|
+
else
|
137
|
+
raise "Option ':link' is required for RSS 1.0"
|
138
|
+
end
|
139
|
+
end
|
140
|
+
maker.channel.generator = "Nitro " + Nitro::Version.to_s
|
141
|
+
|
142
|
+
maker.items.do_sort = true
|
143
|
+
|
144
|
+
# items for each object
|
145
|
+
# * 1.0/0.9/2.0 require @title
|
146
|
+
# * 1.0/0.9 require @link
|
147
|
+
# * 2.0 requires @description
|
148
|
+
objects.each do |o|
|
149
|
+
|
150
|
+
# new Item
|
151
|
+
item = maker.items.new_item
|
152
|
+
|
153
|
+
# Link
|
154
|
+
item.link = "#{options[:base]}/#{o.to_href}" if o.respond_to?(:to_href)
|
155
|
+
item.guid.content = "#{options[:base]}/#{o.to_href}" if options[:version] == '2.0' && o.respond_to?(:to_href)
|
156
|
+
|
157
|
+
# Title
|
158
|
+
item.title = o.title if o.respond_to?(:title)
|
159
|
+
|
160
|
+
# Description
|
161
|
+
if o.respond_to? :body and body = o.body
|
162
|
+
#TODO: think about whether markup should always be done
|
163
|
+
# and whether 256 chars should be a fixed limit
|
164
|
+
#item.description = markup(body.first_char(256))
|
165
|
+
# markup disabled, feedvalidator.org says "description should not contain HTML"
|
166
|
+
# so removing everything that looks like a tag
|
167
|
+
item.description = body.first_char(256).gsub!(/<[^>]+>/, ' ')
|
168
|
+
end
|
169
|
+
|
170
|
+
# Date (item.date asks for a Time object, so don't .to_s !)
|
171
|
+
if o.respond_to?(:update_time)
|
172
|
+
item.date = o.update_time
|
173
|
+
elsif o.respond_to?(:create_time)
|
174
|
+
item.date = o.create_time
|
175
|
+
elsif o.respond_to?(:date)
|
176
|
+
item.date = o.date
|
177
|
+
end
|
178
|
+
|
179
|
+
# Author
|
180
|
+
if o.respond_to?(:author)
|
181
|
+
if o.author[:name]
|
182
|
+
item.author = o.author[:name]
|
183
|
+
end
|
184
|
+
end
|
185
|
+
|
186
|
+
end if objects.size > 0 # objects/items
|
187
|
+
|
188
|
+
# search form
|
189
|
+
maker.textinput.title = options[:search_title] if options[:search_title]
|
190
|
+
maker.textinput.description = options[:search_description] if options[:search_description]
|
191
|
+
maker.textinput.name = options[:search_input_name] if options[:search_input_name]
|
192
|
+
maker.textinput.link = options[:search_form_action] if options[:search_form_action]
|
193
|
+
end
|
194
|
+
|
195
|
+
return rss.to_s
|
196
|
+
end # rss
|
197
|
+
alias_method :rss, :build_rss
|
198
|
+
|
199
|
+
|
200
|
+
# Atom 1.0 feeds.
|
201
|
+
|
202
|
+
def build_atom(objects, options = {})
|
203
|
+
|
204
|
+
# default options
|
205
|
+
options = {
|
206
|
+
:title => 'Syndication',
|
207
|
+
}.update(options)
|
208
|
+
|
209
|
+
raise "first param must be a collection of objects!" unless objects.respond_to?(:to_ary)
|
210
|
+
raise "your object(s) have to respond to :update_time, :create_time or :date" unless objects[0].respond_to?(:update_time) or objects[0].respond_to?(:create_time) or objects[0].respond_to?(:date)
|
211
|
+
raise "Option ':base' cannot be omitted!" unless options[:base]
|
212
|
+
|
213
|
+
# new XML Document for Atom
|
214
|
+
atom = REXML::Document.new
|
215
|
+
atom << REXML::XMLDecl.new("1.0", "utf-8")
|
216
|
+
|
217
|
+
# Root element <feed />
|
218
|
+
feed = REXML::Element.new("feed").add_namespace("http://www.w3.org/2005/Atom")
|
219
|
+
|
220
|
+
# Required feed elements
|
221
|
+
|
222
|
+
# id: Identifies the feed using a universally unique and permanent URI.
|
223
|
+
iduri = URI.parse(options[:id] || options[:base]).normalize.to_s
|
224
|
+
id = REXML::Element.new("id").add_text(iduri)
|
225
|
+
feed << id
|
226
|
+
|
227
|
+
# title: Contains a human readable title for the feed.
|
228
|
+
title = REXML::Element.new("title").add_text(options[:title])
|
229
|
+
feed << title
|
230
|
+
|
231
|
+
# updated: Indicates the last time the feed was modified in a significant way.
|
232
|
+
latest = Time.at(0) # a while back
|
233
|
+
objects.each do |o|
|
234
|
+
if o.respond_to?(:update_time)
|
235
|
+
latest = o.update_time if o.update_time > latest
|
236
|
+
elsif o.respond_to?(:create_time)
|
237
|
+
latest = o.create_time if o.create_time > latest
|
238
|
+
elsif o.respond_to?(:date)
|
239
|
+
latest = o.date if o.date > latest
|
240
|
+
end
|
241
|
+
end
|
242
|
+
updated = REXML::Element.new("updated").add_text(latest.iso8601)
|
243
|
+
feed << updated
|
244
|
+
|
245
|
+
# Recommended feed elements
|
246
|
+
|
247
|
+
# link: A feed should contain a link back to the feed itself.
|
248
|
+
if options[:link]
|
249
|
+
link = REXML::Element.new("link")
|
250
|
+
link.add_attributes({ "rel" => "self", "href" => options[:link] })
|
251
|
+
feed << link
|
252
|
+
end
|
253
|
+
|
254
|
+
# author: Names one author of the feed.
|
255
|
+
if options[:author_name] # name is required for author
|
256
|
+
author = REXML::Element.new("author")
|
257
|
+
author_name = REXML::Element.new("name").add_text(options[:author_name])
|
258
|
+
author << author_name
|
259
|
+
if options[:author_email]
|
260
|
+
author_email = REXML::Element.new("email").add_text(options[:author_email])
|
261
|
+
author << author_email
|
262
|
+
end
|
263
|
+
if options[:author_link]
|
264
|
+
author_link = REXML::Element.new("uri").add_text(options[:author_link])
|
265
|
+
author << author_link
|
266
|
+
end
|
267
|
+
feed << author
|
268
|
+
end
|
269
|
+
|
270
|
+
# Optional feed elements
|
271
|
+
|
272
|
+
# category:
|
273
|
+
# contributor:
|
274
|
+
# generator: Identifies the software used to generate the feed.
|
275
|
+
generator = REXML::Element.new("generator")
|
276
|
+
generator.add_attributes({ "uri" => "http://www.nitroproject.org", "version" => Nitro::Version })
|
277
|
+
generator.add_text("Nitro")
|
278
|
+
feed << generator
|
279
|
+
# icon
|
280
|
+
# logo
|
281
|
+
# rights
|
282
|
+
# subtitle
|
283
|
+
|
284
|
+
# Entries
|
285
|
+
objects.each do |o|
|
286
|
+
|
287
|
+
# new Entry (called "item" in RSS)
|
288
|
+
unless o.respond_to?(:to_href) and o.respond_to?(:title)
|
289
|
+
next
|
290
|
+
end
|
291
|
+
entry = REXML::Element.new("entry")
|
292
|
+
|
293
|
+
# Required entry elements
|
294
|
+
|
295
|
+
# id
|
296
|
+
if o.respond_to?(:to_href)
|
297
|
+
id = REXML::Element.new("id").add_text("#{options[:base]}/#{o.to_href}")
|
298
|
+
entry << id
|
299
|
+
end
|
300
|
+
|
301
|
+
# title
|
302
|
+
if o.respond_to?(:title)
|
303
|
+
title = REXML::Element.new("title").add_text(o.title)
|
304
|
+
entry << title
|
305
|
+
end
|
306
|
+
|
307
|
+
# updated
|
308
|
+
updated = Time.at(0) # a while back
|
309
|
+
if o.respond_to?(:update_time)
|
310
|
+
updated = o.update_time
|
311
|
+
elsif o.respond_to?(:create_time)
|
312
|
+
updated = o.create_time
|
313
|
+
elsif o.respond_to?(:date)
|
314
|
+
updated = o.date
|
315
|
+
end
|
316
|
+
entry << REXML::Element.new("updated").add_text(updated.iso8601)
|
317
|
+
|
318
|
+
# Recommended entry elements
|
319
|
+
|
320
|
+
# author
|
321
|
+
if o.respond_to?(:author)
|
322
|
+
if o.author[:name] # name is required for author
|
323
|
+
author = REXML::Element.new("author")
|
324
|
+
author_name = REXML::Element.new("name").add_text(o.author[:name])
|
325
|
+
author << author_name
|
326
|
+
if o.author[:email]
|
327
|
+
author_email = REXML::Element.new("email").add_text(o.author[:email])
|
328
|
+
author << author_email
|
329
|
+
end
|
330
|
+
if o.author[:link]
|
331
|
+
author_link = REXML::Element.new("uri").add_text(o.author[:link])
|
332
|
+
author << author_link
|
333
|
+
end
|
334
|
+
entry << author
|
335
|
+
end
|
336
|
+
end
|
337
|
+
|
338
|
+
# summary
|
339
|
+
if o.respond_to?(:body)
|
340
|
+
summary = REXML::Element.new("summary")
|
341
|
+
#TODO: think about whether 256 chars should be a fixed limit
|
342
|
+
summary.add_text(o.body.first_char(256).gsub(/<[^>]+>/, ' '))
|
343
|
+
entry << summary
|
344
|
+
end
|
345
|
+
|
346
|
+
# content
|
347
|
+
# may have the type text, html or xhtml
|
348
|
+
if o.respond_to?(:full_content)
|
349
|
+
content = REXML::Element.new("content")
|
350
|
+
#TODO: think about whether markup should always be done
|
351
|
+
content.add_text(markup(o.full_content))
|
352
|
+
entry << content
|
353
|
+
end
|
354
|
+
|
355
|
+
# link: An entry must contain an alternate link if there is no content element.
|
356
|
+
if o.respond_to?(:to_href)
|
357
|
+
link = REXML::Element.new("link")
|
358
|
+
link.add_attributes({ "rel" => "alternate", "href" => "#{options[:base]}/#{o.to_href}" })
|
359
|
+
entry << link
|
360
|
+
end
|
361
|
+
|
362
|
+
# Optional entry elements
|
363
|
+
|
364
|
+
# category
|
365
|
+
# could be used for Tags maybe?
|
366
|
+
# contributor
|
367
|
+
# published
|
368
|
+
if o.respond_to?(:create_time)
|
369
|
+
published = REXML::Element.new("published")
|
370
|
+
published.add_text(o.create_time.iso8601)
|
371
|
+
entry << published
|
372
|
+
end
|
373
|
+
# source
|
374
|
+
# rights
|
375
|
+
|
376
|
+
# don't forget to add the entry to the feed
|
377
|
+
feed << entry
|
378
|
+
|
379
|
+
end if objects.size > 0 # objects/entries
|
380
|
+
|
381
|
+
atom << feed
|
382
|
+
|
383
|
+
return atom.to_s
|
384
|
+
end # atom
|
385
|
+
alias_method :atom, :build_atom
|
386
|
+
|
387
|
+
# OPML 1.0 feed lists
|
388
|
+
# Fabian: eww, who invented OPML? Who needs it? Implementing
|
389
|
+
# it in a very rough way anyway though. Takes a Hash of
|
390
|
+
# Feeds and optional options.
|
391
|
+
|
392
|
+
def build_opml(feedhash, options = {})
|
393
|
+
|
394
|
+
# new XML Document for OPML
|
395
|
+
opml = REXML::Document.new
|
396
|
+
opml << REXML::XMLDecl.new("1.0", "utf-8")
|
397
|
+
|
398
|
+
# Root element <opml />
|
399
|
+
opml = REXML::Element.new("opml")
|
400
|
+
opml.add_attribute("version", "1.0")
|
401
|
+
|
402
|
+
# head
|
403
|
+
head = REXML::Element.new("head")
|
404
|
+
# title
|
405
|
+
if options[:title]
|
406
|
+
title = REXML::Element.new("title").add_text(options[:title])
|
407
|
+
head << title
|
408
|
+
end
|
409
|
+
# dateCreated
|
410
|
+
# dateModified
|
411
|
+
# ownerName
|
412
|
+
# ownerEmail
|
413
|
+
opml << head
|
414
|
+
|
415
|
+
# body
|
416
|
+
body = REXML::Element.new("body")
|
417
|
+
feedhash.each do |url, type|
|
418
|
+
outline = REXML::Element.new("outline")
|
419
|
+
outline.add_attributes({ "type" => type, "xmlUrl" => url })
|
420
|
+
body << outline
|
421
|
+
end
|
422
|
+
opml << body
|
423
|
+
|
424
|
+
return opml.to_s
|
425
|
+
end # opml
|
426
|
+
alias_method :opml, :build_opml
|
427
|
+
|
428
|
+
end
|
429
|
+
end
|
430
|
+
|
431
|
+
# * Fabian Buch <fabian@fabian-buch.de>
|
432
|
+
# * George Moschovitis <gm@navel.gr>
|
data/lib/nitro/helper/form.rb
CHANGED
@@ -1,6 +1,7 @@
|
|
1
1
|
require 'facet/inflect'
|
2
2
|
|
3
3
|
require 'nitro/helper/form/controls'
|
4
|
+
require 'nitro/helper/form/builder'
|
4
5
|
|
5
6
|
module Nitro
|
6
7
|
|
@@ -86,9 +87,13 @@ module FormHelper
|
|
86
87
|
end
|
87
88
|
end
|
88
89
|
|
89
|
-
|
90
|
+
if action == :self
|
91
|
+
str << %{<form method="post"#{enctype_attribute}>}
|
92
|
+
else
|
93
|
+
action = "#{@base}/#{action}" unless action =~ /\//
|
94
|
+
str << %{<form action="#{action}" method="post"#{enctype_attribute}>}
|
95
|
+
end
|
90
96
|
|
91
|
-
str << %{<form action="#{action}" method="post"#{enctype_attribute}>}
|
92
97
|
str << %{<input type="hidden" name="oid" value="#{obj.oid}" />} if obj.oid
|
93
98
|
str << controls_for(obj, options)
|
94
99
|
str << %{<input type="submit" value="#{submit}" />}
|
@@ -104,7 +109,10 @@ module FormHelper
|
|
104
109
|
str = FormBuilder.prologue
|
105
110
|
|
106
111
|
controls_for_properties(str, obj, options)
|
107
|
-
|
112
|
+
|
113
|
+
unless options[:no_relations]
|
114
|
+
controls_for_relations(str, obj, options)
|
115
|
+
end
|
108
116
|
|
109
117
|
str << FormBuilder.epilogue
|
110
118
|
|