nitro 0.29.0 → 0.30.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (93) hide show
  1. data/CHANGELOG +410 -0
  2. data/ProjectInfo +36 -44
  3. data/README +5 -5
  4. data/doc/AUTHORS +6 -0
  5. data/doc/RELEASES +159 -2
  6. data/lib/glue/sweeper.rb +2 -2
  7. data/lib/glue/webfile.rb +14 -1
  8. data/lib/nitro.rb +6 -9
  9. data/lib/nitro/adapter/mongrel.rb +36 -43
  10. data/lib/nitro/adapter/scgi.rb +1 -1
  11. data/lib/nitro/adapter/webrick.rb +96 -24
  12. data/lib/nitro/caching/actions.rb +2 -1
  13. data/lib/nitro/caching/fragments.rb +1 -8
  14. data/lib/nitro/caching/output.rb +14 -4
  15. data/lib/nitro/cgi.rb +19 -21
  16. data/lib/nitro/cgi/cookie.rb +5 -1
  17. data/lib/nitro/cgi/request.rb +20 -4
  18. data/lib/nitro/compiler.rb +74 -28
  19. data/lib/nitro/compiler/cleanup.rb +1 -1
  20. data/lib/nitro/compiler/elements.rb +1 -2
  21. data/lib/nitro/compiler/localization.rb +1 -1
  22. data/lib/nitro/compiler/markup.rb +1 -1
  23. data/lib/nitro/compiler/script.rb +52 -44
  24. data/lib/nitro/compiler/squeeze.rb +4 -3
  25. data/lib/nitro/compiler/xslt.rb +7 -6
  26. data/lib/nitro/context.rb +39 -20
  27. data/lib/nitro/controller.rb +24 -5
  28. data/lib/nitro/dispatcher.rb +13 -5
  29. data/lib/nitro/global.rb +63 -0
  30. data/lib/nitro/helper/feed.rb +432 -0
  31. data/lib/nitro/helper/form.rb +11 -3
  32. data/lib/nitro/helper/form/builder.rb +140 -0
  33. data/lib/nitro/helper/form/controls.rb +2 -1
  34. data/lib/nitro/helper/javascript.rb +6 -0
  35. data/lib/nitro/helper/javascript/morphing.rb +13 -6
  36. data/lib/nitro/helper/xhtml.rb +42 -6
  37. data/lib/nitro/helper/xml.rb +3 -0
  38. data/lib/nitro/part.rb +2 -2
  39. data/lib/nitro/render.rb +7 -2
  40. data/lib/nitro/router.rb +57 -16
  41. data/lib/nitro/scaffolding.rb +29 -20
  42. data/lib/nitro/server.rb +4 -10
  43. data/lib/nitro/server/drb.rb +1 -1
  44. data/lib/nitro/server/runner.rb +10 -0
  45. data/lib/nitro/session.rb +31 -12
  46. data/lib/nitro/session/drb.rb +13 -1
  47. data/lib/nitro/session/file.rb +1 -1
  48. data/lib/nitro/session/memcached.rb +1 -1
  49. data/lib/nitro/session/memory.rb +1 -1
  50. data/lib/nitro/session/og.rb +1 -1
  51. data/lib/nitro/test/testcase.rb +3 -0
  52. data/proto/public/error.xhtml +5 -5
  53. data/proto/public/js/controls.js +2 -2
  54. data/proto/public/js/dragdrop.js +320 -79
  55. data/proto/public/js/effects.js +200 -152
  56. data/proto/public/js/prototype.js +284 -63
  57. data/proto/public/js/scriptaculous.js +7 -5
  58. data/proto/public/js/unittest.js +11 -0
  59. data/proto/public/scaffold/advanced_search.xhtml +30 -0
  60. data/proto/public/scaffold/list.xhtml +8 -1
  61. data/proto/public/scaffold/search.xhtml +2 -1
  62. data/proto/script/scgi_service +1 -1
  63. data/src/part/admin/controller.rb +1 -1
  64. data/src/part/admin/skin.rb +1 -1
  65. data/test/nitro/CONFIG.rb +3 -0
  66. data/test/nitro/adapter/tc_webrick.rb +1 -1
  67. data/test/nitro/cgi/tc_cookie.rb +1 -1
  68. data/test/nitro/cgi/tc_request.rb +5 -5
  69. data/test/nitro/compiler/tc_client_morpher.rb +47 -0
  70. data/test/nitro/compiler/tc_compiler.rb +2 -0
  71. data/test/nitro/helper/tc_feed.rb +138 -0
  72. data/test/nitro/helper/tc_pager.rb +1 -1
  73. data/test/nitro/helper/tc_rss.rb +1 -1
  74. data/test/nitro/helper/tc_table.rb +1 -1
  75. data/test/nitro/helper/tc_xhtml.rb +1 -1
  76. data/test/nitro/tc_caching.rb +1 -1
  77. data/test/nitro/tc_cgi.rb +1 -1
  78. data/test/nitro/tc_context.rb +1 -1
  79. data/test/nitro/tc_controller.rb +31 -3
  80. data/test/nitro/tc_controller_aspect.rb +1 -1
  81. data/test/nitro/tc_dispatcher.rb +1 -1
  82. data/test/nitro/tc_element.rb +1 -1
  83. data/test/nitro/tc_flash.rb +1 -1
  84. data/test/nitro/tc_helper.rb +1 -1
  85. data/test/nitro/tc_render.rb +6 -6
  86. data/test/nitro/tc_router.rb +8 -4
  87. data/test/nitro/tc_server.rb +1 -3
  88. data/test/nitro/tc_session.rb +1 -3
  89. metadata +107 -104
  90. data/Rakefile +0 -232
  91. data/lib/nitro/adapter/acgi.rb +0 -237
  92. data/proto/public/Makefile.acgi +0 -40
  93. 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>
@@ -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
- action = "#{@base}/#{action}" unless action =~ /\//
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
- controls_for_relations(str, obj, options)
112
+
113
+ unless options[:no_relations]
114
+ controls_for_relations(str, obj, options)
115
+ end
108
116
 
109
117
  str << FormBuilder.epilogue
110
118