raw 0.49.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/doc/CONTRIBUTORS +106 -0
- data/doc/LICENSE +32 -0
- data/doc/coding_conventions.txt +11 -0
- data/lib/raw.rb +42 -0
- data/lib/raw/adapter.rb +113 -0
- data/lib/raw/adapter/cgi.rb +41 -0
- data/lib/raw/adapter/fastcgi.rb +48 -0
- data/lib/raw/adapter/mongrel.rb +146 -0
- data/lib/raw/adapter/script.rb +94 -0
- data/lib/raw/adapter/webrick.rb +144 -0
- data/lib/raw/adapter/webrick/vcr.rb +91 -0
- data/lib/raw/cgi.rb +323 -0
- data/lib/raw/cgi/cookie.rb +47 -0
- data/lib/raw/cgi/http.rb +62 -0
- data/lib/raw/compiler.rb +138 -0
- data/lib/raw/compiler/filter/cleanup.rb +21 -0
- data/lib/raw/compiler/filter/elements.rb +166 -0
- data/lib/raw/compiler/filter/elements/element.rb +210 -0
- data/lib/raw/compiler/filter/localization.rb +23 -0
- data/lib/raw/compiler/filter/markup.rb +32 -0
- data/lib/raw/compiler/filter/morph.rb +123 -0
- data/lib/raw/compiler/filter/morph/each.rb +34 -0
- data/lib/raw/compiler/filter/morph/for.rb +11 -0
- data/lib/raw/compiler/filter/morph/if.rb +26 -0
- data/lib/raw/compiler/filter/morph/selected_if.rb +43 -0
- data/lib/raw/compiler/filter/morph/standard.rb +55 -0
- data/lib/raw/compiler/filter/morph/times.rb +27 -0
- data/lib/raw/compiler/filter/script.rb +116 -0
- data/lib/raw/compiler/filter/squeeze.rb +16 -0
- data/lib/raw/compiler/filter/static_include.rb +74 -0
- data/lib/raw/compiler/filter/template.rb +121 -0
- data/lib/raw/compiler/reloader.rb +96 -0
- data/lib/raw/context.rb +154 -0
- data/lib/raw/context/flash.rb +157 -0
- data/lib/raw/context/global.rb +88 -0
- data/lib/raw/context/request.rb +338 -0
- data/lib/raw/context/response.rb +57 -0
- data/lib/raw/context/session.rb +198 -0
- data/lib/raw/context/session/drb.rb +11 -0
- data/lib/raw/context/session/file.rb +15 -0
- data/lib/raw/context/session/memcached.rb +13 -0
- data/lib/raw/context/session/memory.rb +12 -0
- data/lib/raw/context/session/og.rb +15 -0
- data/lib/raw/context/session/pstore.rb +13 -0
- data/lib/raw/control.rb +18 -0
- data/lib/raw/control/attribute.rb +91 -0
- data/lib/raw/control/attribute/checkbox.rb +25 -0
- data/lib/raw/control/attribute/datetime.rb +21 -0
- data/lib/raw/control/attribute/file.rb +20 -0
- data/lib/raw/control/attribute/fixnum.rb +26 -0
- data/lib/raw/control/attribute/float.rb +26 -0
- data/lib/raw/control/attribute/options.rb +38 -0
- data/lib/raw/control/attribute/password.rb +16 -0
- data/lib/raw/control/attribute/text.rb +16 -0
- data/lib/raw/control/attribute/textarea.rb +16 -0
- data/lib/raw/control/none.rb +16 -0
- data/lib/raw/control/relation.rb +59 -0
- data/lib/raw/control/relation/belongs_to.rb +0 -0
- data/lib/raw/control/relation/has_many.rb +97 -0
- data/lib/raw/control/relation/joins_many.rb +0 -0
- data/lib/raw/control/relation/many_to_many.rb +0 -0
- data/lib/raw/control/relation/refers_to.rb +29 -0
- data/lib/raw/controller.rb +37 -0
- data/lib/raw/controller/publishable.rb +160 -0
- data/lib/raw/dispatcher.rb +209 -0
- data/lib/raw/dispatcher/format.rb +108 -0
- data/lib/raw/dispatcher/format/atom.rb +31 -0
- data/lib/raw/dispatcher/format/css.rb +0 -0
- data/lib/raw/dispatcher/format/html.rb +42 -0
- data/lib/raw/dispatcher/format/json.rb +31 -0
- data/lib/raw/dispatcher/format/rss.rb +33 -0
- data/lib/raw/dispatcher/format/xoxo.rb +31 -0
- data/lib/raw/dispatcher/mounter.rb +60 -0
- data/lib/raw/dispatcher/router.rb +111 -0
- data/lib/raw/errors.rb +19 -0
- data/lib/raw/helper.rb +86 -0
- data/lib/raw/helper/benchmark.rb +23 -0
- data/lib/raw/helper/buffer.rb +60 -0
- data/lib/raw/helper/cookie.rb +32 -0
- data/lib/raw/helper/debug.rb +28 -0
- data/lib/raw/helper/default.rb +16 -0
- data/lib/raw/helper/feed.rb +451 -0
- data/lib/raw/helper/form.rb +284 -0
- data/lib/raw/helper/javascript.rb +59 -0
- data/lib/raw/helper/layout.rb +40 -0
- data/lib/raw/helper/navigation.rb +87 -0
- data/lib/raw/helper/pager.rb +305 -0
- data/lib/raw/helper/table.rb +247 -0
- data/lib/raw/helper/xhtml.rb +218 -0
- data/lib/raw/helper/xml.rb +125 -0
- data/lib/raw/mixin/magick.rb +35 -0
- data/lib/raw/mixin/sweeper.rb +71 -0
- data/lib/raw/mixin/thumbnails.rb +1 -0
- data/lib/raw/mixin/webfile.rb +165 -0
- data/lib/raw/render.rb +271 -0
- data/lib/raw/render/builder.rb +26 -0
- data/lib/raw/render/caching.rb +81 -0
- data/lib/raw/render/call.rb +43 -0
- data/lib/raw/render/send_file.rb +46 -0
- data/lib/raw/render/stream.rb +39 -0
- data/lib/raw/scaffold.rb +13 -0
- data/lib/raw/scaffold/controller.rb +25 -0
- data/lib/raw/scaffold/model.rb +157 -0
- data/lib/raw/test.rb +5 -0
- data/lib/raw/test/assertions.rb +169 -0
- data/lib/raw/test/context.rb +55 -0
- data/lib/raw/test/testcase.rb +79 -0
- data/lib/raw/util/attr.rb +128 -0
- data/lib/raw/util/encode_uri.rb +149 -0
- data/lib/raw/util/html_filter.rb +538 -0
- data/lib/raw/util/markup.rb +130 -0
- data/test/glue/tc_webfile.rb +1 -0
- data/test/nitro/CONFIG.rb +3 -0
- data/test/nitro/adapter/raw_post1.bin +9 -0
- data/test/nitro/adapter/tc_webrick.rb +16 -0
- data/test/nitro/cgi/tc_cookie.rb +14 -0
- data/test/nitro/cgi/tc_request.rb +61 -0
- data/test/nitro/compiler/tc_client_morpher.rb +47 -0
- data/test/nitro/compiler/tc_compiler.rb +25 -0
- data/test/nitro/dispatcher/tc_mounter.rb +47 -0
- data/test/nitro/helper/tc_feed.rb +135 -0
- data/test/nitro/helper/tc_navbar.rb +74 -0
- data/test/nitro/helper/tc_pager.rb +35 -0
- data/test/nitro/helper/tc_table.rb +68 -0
- data/test/nitro/helper/tc_xhtml.rb +19 -0
- data/test/nitro/tc_caching.rb +19 -0
- data/test/nitro/tc_cgi.rb +222 -0
- data/test/nitro/tc_context.rb +17 -0
- data/test/nitro/tc_controller.rb +103 -0
- data/test/nitro/tc_controller_aspect.rb +32 -0
- data/test/nitro/tc_controller_params.rb +885 -0
- data/test/nitro/tc_dispatcher.rb +109 -0
- data/test/nitro/tc_element.rb +85 -0
- data/test/nitro/tc_flash.rb +59 -0
- data/test/nitro/tc_helper.rb +47 -0
- data/test/nitro/tc_render.rb +119 -0
- data/test/nitro/tc_router.rb +61 -0
- data/test/nitro/tc_server.rb +35 -0
- data/test/nitro/tc_session.rb +66 -0
- data/test/nitro/tc_template.rb +71 -0
- data/test/nitro/util/tc_encode_url.rb +87 -0
- data/test/nitro/util/tc_markup.rb +31 -0
- data/test/public/blog/another/very_litle/index.xhtml +1 -0
- data/test/public/blog/inc1.xhtml +2 -0
- data/test/public/blog/inc2.xhtml +1 -0
- data/test/public/blog/list.xhtml +9 -0
- data/test/public/dummy_mailer/registration.xhtml +5 -0
- metadata +244 -0
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
require "benchmark"
|
|
2
|
+
|
|
3
|
+
module Raw
|
|
4
|
+
|
|
5
|
+
# This helper adds benchmarking support in your Controllers.
|
|
6
|
+
# Useful for fine tuning and optimizing your actions.
|
|
7
|
+
|
|
8
|
+
module BenchmarkHelper
|
|
9
|
+
|
|
10
|
+
# Log real time spent on a task.
|
|
11
|
+
#
|
|
12
|
+
# === Example
|
|
13
|
+
#
|
|
14
|
+
# benchmark "Doing an operation" { operation }
|
|
15
|
+
|
|
16
|
+
def benchmark(message = "Benchmarking")
|
|
17
|
+
real = Benchmark.realtime { yield }
|
|
18
|
+
info "#{message}: time = #{'%.5f' % real} ms."
|
|
19
|
+
end
|
|
20
|
+
|
|
21
|
+
end
|
|
22
|
+
|
|
23
|
+
end
|
|
@@ -0,0 +1,60 @@
|
|
|
1
|
+
module Raw
|
|
2
|
+
|
|
3
|
+
# The output buffering mixin. Provides php-style output
|
|
4
|
+
# buffering functionality.
|
|
5
|
+
#
|
|
6
|
+
# === Examples
|
|
7
|
+
#
|
|
8
|
+
# <?r buf = capture do ?>
|
|
9
|
+
# ...
|
|
10
|
+
# <?r end ?>
|
|
11
|
+
#--
|
|
12
|
+
# TODO: use better names but keep the ob_xxx php style methods
|
|
13
|
+
# as aliases.
|
|
14
|
+
#++
|
|
15
|
+
|
|
16
|
+
module BufferHelper
|
|
17
|
+
|
|
18
|
+
private
|
|
19
|
+
|
|
20
|
+
# Output buffers stack, used for php-style nested output
|
|
21
|
+
# buffering.
|
|
22
|
+
|
|
23
|
+
def out_buffers; @out_buffers; end
|
|
24
|
+
|
|
25
|
+
# Start (push) a new output buffer.
|
|
26
|
+
|
|
27
|
+
def open_buffer
|
|
28
|
+
@out_buffers ||= []
|
|
29
|
+
@out_buffers.push(@out)
|
|
30
|
+
@out = ""
|
|
31
|
+
end
|
|
32
|
+
alias_method :ob_start, :open_buffer
|
|
33
|
+
|
|
34
|
+
# End (pop) the current output buffer.
|
|
35
|
+
|
|
36
|
+
def close_buffer
|
|
37
|
+
buf = @out
|
|
38
|
+
@out = @out_buffers.pop
|
|
39
|
+
return buf
|
|
40
|
+
end
|
|
41
|
+
alias_method :ob_end, :close_buffer
|
|
42
|
+
|
|
43
|
+
# End (pop) the current output buffer and write to the parent.
|
|
44
|
+
|
|
45
|
+
def close_and_write_buffer
|
|
46
|
+
nested_buffer = @out
|
|
47
|
+
@out = @out_buffers.pop
|
|
48
|
+
@out << nested_buffer
|
|
49
|
+
end
|
|
50
|
+
alias_method :ob_write_end, :close_and_write_buffer
|
|
51
|
+
|
|
52
|
+
def capture
|
|
53
|
+
open_buffer
|
|
54
|
+
yield
|
|
55
|
+
return close_buffer
|
|
56
|
+
end
|
|
57
|
+
|
|
58
|
+
end
|
|
59
|
+
|
|
60
|
+
end
|
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
require "raw/cgi/cookie"
|
|
2
|
+
|
|
3
|
+
module Raw
|
|
4
|
+
|
|
5
|
+
# Cookie related utility methods.
|
|
6
|
+
|
|
7
|
+
module CookieHelper
|
|
8
|
+
|
|
9
|
+
private
|
|
10
|
+
|
|
11
|
+
def cookies
|
|
12
|
+
@context.cookies
|
|
13
|
+
end
|
|
14
|
+
|
|
15
|
+
# Send the cookie to the response stream.
|
|
16
|
+
|
|
17
|
+
def send_cookie(name, value = nil)
|
|
18
|
+
@context.add_cookie(name, value)
|
|
19
|
+
end
|
|
20
|
+
|
|
21
|
+
# Delete the cookie by setting the expire time to now and
|
|
22
|
+
# clearing the value.
|
|
23
|
+
|
|
24
|
+
def delete_cookie(name)
|
|
25
|
+
cookie = Cookie.new(name, "")
|
|
26
|
+
cookie.expires = Time.now
|
|
27
|
+
@context.add_cookie(cookie)
|
|
28
|
+
end
|
|
29
|
+
|
|
30
|
+
end
|
|
31
|
+
|
|
32
|
+
end
|
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
module Raw
|
|
2
|
+
|
|
3
|
+
# A collection of useful debuging methods.
|
|
4
|
+
|
|
5
|
+
module DebugHelper
|
|
6
|
+
|
|
7
|
+
private
|
|
8
|
+
|
|
9
|
+
# Returns a <pre>-tag set with the +object+ dumped by YAML.
|
|
10
|
+
# Very readable way to inspect an object.
|
|
11
|
+
#--
|
|
12
|
+
# TODO: make safe html.
|
|
13
|
+
#++
|
|
14
|
+
|
|
15
|
+
def debug(object)
|
|
16
|
+
begin
|
|
17
|
+
Marshal::dump(object)
|
|
18
|
+
"<pre class='debug_dump'>#{object.to_yaml.gsub(" ", " ")}</pre>"
|
|
19
|
+
rescue TypeError => ex
|
|
20
|
+
# Object couldn't be dumped, perhaps because of singleton
|
|
21
|
+
# methods, this is the fallback.
|
|
22
|
+
"<code class='debug_dump'>#{object.inspect}</code>"
|
|
23
|
+
end
|
|
24
|
+
end
|
|
25
|
+
|
|
26
|
+
end
|
|
27
|
+
|
|
28
|
+
end
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
require "raw/helper"
|
|
2
|
+
require "raw/helper/debug"
|
|
3
|
+
|
|
4
|
+
module Raw
|
|
5
|
+
|
|
6
|
+
# This helper is included by default in all Controllers.
|
|
7
|
+
# This is the place to add general purpose utility methods to
|
|
8
|
+
# be shared accross all Controllers.
|
|
9
|
+
|
|
10
|
+
module DefaultHelper
|
|
11
|
+
helper DebugHelper
|
|
12
|
+
|
|
13
|
+
# This is an open module, extend in your application.
|
|
14
|
+
end
|
|
15
|
+
|
|
16
|
+
end
|
|
@@ -0,0 +1,451 @@
|
|
|
1
|
+
require 'rss/maker'
|
|
2
|
+
require 'rexml/document'
|
|
3
|
+
require 'time'
|
|
4
|
+
require 'uri'
|
|
5
|
+
|
|
6
|
+
require 'facets/core/string/first_char'
|
|
7
|
+
|
|
8
|
+
require 'raw/util/markup'
|
|
9
|
+
|
|
10
|
+
module Raw
|
|
11
|
+
|
|
12
|
+
# A helper that provides Feed related methods.
|
|
13
|
+
#
|
|
14
|
+
# To include this helper into your controller,
|
|
15
|
+
# add the following at the beginning of your controller:
|
|
16
|
+
#
|
|
17
|
+
# helper :feed
|
|
18
|
+
#
|
|
19
|
+
# Then define actions that set an appropriate content_type and use build_(rss|atom|opml)
|
|
20
|
+
# to generate your desired feed. See below for details.
|
|
21
|
+
#
|
|
22
|
+
# == RSS 0.91, 1.0 and 2.0
|
|
23
|
+
#
|
|
24
|
+
# response.content_type = "application/rss+xml"
|
|
25
|
+
# build_rss(og_objects,
|
|
26
|
+
# :version => "0.9",
|
|
27
|
+
# :base => context.host_uri, # + object.to_href results in an item-link
|
|
28
|
+
# :link => context.host_uri+"/feed", # link to this feed
|
|
29
|
+
# :title => "Feed Title",
|
|
30
|
+
# :description => "What this feed is about",
|
|
31
|
+
# :search_title => "Search Form Here",
|
|
32
|
+
# :search_description => "Search description",
|
|
33
|
+
# :search_input_name => "search_field",
|
|
34
|
+
# :search_form_action => "http://uri/to/search_action"
|
|
35
|
+
# )
|
|
36
|
+
#
|
|
37
|
+
# For RSS 1.0 or RSS 2.0 just change :version (defaults to '0.91'),
|
|
38
|
+
# possible :version options are "0.9", "0.91", "1.0" and "2.0"
|
|
39
|
+
#
|
|
40
|
+
# * for RSS 0.9 :language is required (or defaults to 'en')
|
|
41
|
+
# * for all RSS versions :title, :link and/or :base, :description are required
|
|
42
|
+
#
|
|
43
|
+
# <b>individual objects have to respond to at least:</b>
|
|
44
|
+
#
|
|
45
|
+
# * 1.0/0.9/2.0 require @title
|
|
46
|
+
# * 1.0/0.9 require @to_href
|
|
47
|
+
# * 2.0 requires @body
|
|
48
|
+
#
|
|
49
|
+
# if it doesn't, no item is created
|
|
50
|
+
#
|
|
51
|
+
# * @update_time, @create_time or @date is used for item.date
|
|
52
|
+
# * so if Og's "is Timestamped" is being used, it'll be @update_time
|
|
53
|
+
# * @author.name can optionally be used for item.author
|
|
54
|
+
#
|
|
55
|
+
# == Atom 1.0
|
|
56
|
+
#
|
|
57
|
+
# response.content_type = "application/atom+xml"
|
|
58
|
+
# build_atom(og_objects,
|
|
59
|
+
# :title => "Feed Title",
|
|
60
|
+
# :base => context.host_uri, # + object.to_href results in an item-link
|
|
61
|
+
# :link => context.host_uri+"/atomfeed",
|
|
62
|
+
# :id => "your_unique_id", # :base is being used unless :id specified (:base is recommended)
|
|
63
|
+
# :author_name => "Takeo",
|
|
64
|
+
# :author_email => "email@example.com",
|
|
65
|
+
# :author_link => "http://uri.to/authors/home",
|
|
66
|
+
# )
|
|
67
|
+
#
|
|
68
|
+
# <b>individual objects have to respond to at least:</b>
|
|
69
|
+
#
|
|
70
|
+
# * @title
|
|
71
|
+
# * @to_href
|
|
72
|
+
# * @update_time/@create_time/@date (at least one of them)
|
|
73
|
+
#
|
|
74
|
+
# if it doesn't, no entry is created
|
|
75
|
+
#
|
|
76
|
+
# optional:
|
|
77
|
+
#
|
|
78
|
+
# * @body (taken as summary (256 chars))
|
|
79
|
+
# * @full_content # can countain html
|
|
80
|
+
# * use Og's "is Timestamped", so both @update_time and @create_time can be used
|
|
81
|
+
# * @author.name
|
|
82
|
+
# * @author.link
|
|
83
|
+
# * @author.email # be careful, you don't want to publish your users email address to spammers
|
|
84
|
+
#
|
|
85
|
+
#
|
|
86
|
+
# == OPML 1.0 feed lists
|
|
87
|
+
#
|
|
88
|
+
# Fabian: Eew, who invented OPML? Who needs it? Implementing it in a very rough way anyway though.
|
|
89
|
+
# takes a Hash of Feeds and optional options
|
|
90
|
+
#
|
|
91
|
+
# response.content_type = "application/opml+xml"
|
|
92
|
+
# build_opml(
|
|
93
|
+
# {
|
|
94
|
+
# "http://oxyliquit.de/feed" => "rss",
|
|
95
|
+
# "http://oxyliquit.de/feed/questions" => "rss",
|
|
96
|
+
# "http://oxyliquit.de/feed/tips" => "rss",
|
|
97
|
+
# "http://oxyliquit.de/feed/tutorials" => "rss"
|
|
98
|
+
# },
|
|
99
|
+
# :title => "My feeds"
|
|
100
|
+
# )
|
|
101
|
+
|
|
102
|
+
module FeedHelper
|
|
103
|
+
include Nitro::Markup
|
|
104
|
+
|
|
105
|
+
# RSS 0.91, 1.0, 2.0 feeds.
|
|
106
|
+
|
|
107
|
+
def build_rss(objects, options = {})
|
|
108
|
+
|
|
109
|
+
# default options
|
|
110
|
+
options = {
|
|
111
|
+
:title => 'Syndication',
|
|
112
|
+
:description => 'Syndication',
|
|
113
|
+
:version => '0.9',
|
|
114
|
+
:language => 'en', # required by 0.9
|
|
115
|
+
}.update(options)
|
|
116
|
+
|
|
117
|
+
raise "Option ':version' contains a wrong version!" unless %w(0.9 0.91 1.0 2.0).include?(options[:version])
|
|
118
|
+
|
|
119
|
+
options[:base] ||= options[:link]
|
|
120
|
+
raise "Option ':base' cannot be omitted!" unless options[:base]
|
|
121
|
+
|
|
122
|
+
# build rss
|
|
123
|
+
rss = RSS::Maker.make(options[:version]) do |maker|
|
|
124
|
+
maker.channel.title = options[:title]
|
|
125
|
+
maker.channel.description = options[:description]
|
|
126
|
+
if options[:link]
|
|
127
|
+
maker.channel.link = options[:link]
|
|
128
|
+
else
|
|
129
|
+
maker.channel.link = options[:base] #FIXME: not sure
|
|
130
|
+
end
|
|
131
|
+
case options[:version]
|
|
132
|
+
when '0.9', '0.91'
|
|
133
|
+
maker.channel.language = options[:language]
|
|
134
|
+
when '1.0'
|
|
135
|
+
if options[:link]
|
|
136
|
+
maker.channel.about = options[:link]
|
|
137
|
+
else
|
|
138
|
+
raise "Option ':link' is required for RSS 1.0"
|
|
139
|
+
end
|
|
140
|
+
end
|
|
141
|
+
maker.channel.generator = "Nitro #{Nitro::Version}"
|
|
142
|
+
|
|
143
|
+
maker.items.do_sort = true
|
|
144
|
+
|
|
145
|
+
# items for each object
|
|
146
|
+
# * 1.0/0.9/2.0 require @title
|
|
147
|
+
# * 1.0/0.9 require @link
|
|
148
|
+
# * 2.0 requires @description
|
|
149
|
+
objects.each do |o|
|
|
150
|
+
|
|
151
|
+
# new Item
|
|
152
|
+
item = maker.items.new_item
|
|
153
|
+
|
|
154
|
+
# Link
|
|
155
|
+
item.link = "#{options[:base]}#{fhref o}" if o.respond_to?(:to_href)
|
|
156
|
+
item.guid.content = "#{options[:base]}#{fhref o}" if options[:version] == '2.0' && o.respond_to?(:to_href)
|
|
157
|
+
|
|
158
|
+
# Title
|
|
159
|
+
item.title = o.title if o.respond_to?(:title)
|
|
160
|
+
|
|
161
|
+
# Description
|
|
162
|
+
if o.respond_to? :body and body = o.body
|
|
163
|
+
#TODO: think about whether markup should always be done
|
|
164
|
+
# and whether 256 chars should be a fixed limit
|
|
165
|
+
#item.description = markup(body.first_char(256))
|
|
166
|
+
# markup disabled, feedvalidator.org says "description should not contain HTML"
|
|
167
|
+
# so removing everything that looks like a tag
|
|
168
|
+
item.description = body.first_char(256).gsub!(/<[^>]+>/, ' ')
|
|
169
|
+
end
|
|
170
|
+
|
|
171
|
+
# Date (item.date asks for a Time object, so don't .to_s !)
|
|
172
|
+
if o.respond_to?(:update_time)
|
|
173
|
+
item.date = o.update_time
|
|
174
|
+
elsif o.respond_to?(:create_time)
|
|
175
|
+
item.date = o.create_time
|
|
176
|
+
elsif o.respond_to?(:date)
|
|
177
|
+
item.date = o.date
|
|
178
|
+
end
|
|
179
|
+
|
|
180
|
+
# Author. Use .to_s to be more flexible.
|
|
181
|
+
|
|
182
|
+
if o.respond_to?(:author)
|
|
183
|
+
if o.author.respond_to?(:name)
|
|
184
|
+
item.author = o.author.name
|
|
185
|
+
else
|
|
186
|
+
item.author = o.author.to_s
|
|
187
|
+
end
|
|
188
|
+
end
|
|
189
|
+
|
|
190
|
+
end if objects.size > 0 # objects/items
|
|
191
|
+
|
|
192
|
+
# search form
|
|
193
|
+
maker.textinput.title = options[:search_title] if options[:search_title]
|
|
194
|
+
maker.textinput.description = options[:search_description] if options[:search_description]
|
|
195
|
+
maker.textinput.name = options[:search_input_name] if options[:search_input_name]
|
|
196
|
+
maker.textinput.link = options[:search_form_action] if options[:search_form_action]
|
|
197
|
+
end
|
|
198
|
+
|
|
199
|
+
return rss.to_s
|
|
200
|
+
end
|
|
201
|
+
alias_method :rss, :build_rss
|
|
202
|
+
|
|
203
|
+
|
|
204
|
+
# Atom 1.0 feeds.
|
|
205
|
+
|
|
206
|
+
def build_atom(objects, options = {})
|
|
207
|
+
|
|
208
|
+
# default options
|
|
209
|
+
options = {
|
|
210
|
+
:title => 'Syndication',
|
|
211
|
+
}.update(options)
|
|
212
|
+
|
|
213
|
+
raise "first param must be a collection of objects!" unless objects.respond_to?(:to_ary)
|
|
214
|
+
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)
|
|
215
|
+
raise "Option ':base' cannot be omitted!" unless options[:base]
|
|
216
|
+
|
|
217
|
+
# new XML Document for Atom
|
|
218
|
+
atom = REXML::Document.new
|
|
219
|
+
atom << REXML::XMLDecl.new("1.0", "utf-8")
|
|
220
|
+
|
|
221
|
+
# Root element <feed />
|
|
222
|
+
feed = REXML::Element.new("feed").add_namespace("http://www.w3.org/2005/Atom")
|
|
223
|
+
|
|
224
|
+
# Required feed elements
|
|
225
|
+
|
|
226
|
+
# id: Identifies the feed using a universally unique and permanent URI.
|
|
227
|
+
iduri = URI.parse(options[:id] || options[:base]).normalize.to_s
|
|
228
|
+
id = REXML::Element.new("id").add_text(iduri)
|
|
229
|
+
feed << id
|
|
230
|
+
|
|
231
|
+
# title: Contains a human readable title for the feed.
|
|
232
|
+
title = REXML::Element.new("title").add_text(options[:title])
|
|
233
|
+
feed << title
|
|
234
|
+
|
|
235
|
+
# updated: Indicates the last time the feed was modified in a significant way.
|
|
236
|
+
latest = Time.at(0) # a while back
|
|
237
|
+
objects.each do |o|
|
|
238
|
+
if o.respond_to?(:update_time)
|
|
239
|
+
latest = o.update_time if o.update_time > latest
|
|
240
|
+
elsif o.respond_to?(:create_time)
|
|
241
|
+
latest = o.create_time if o.create_time > latest
|
|
242
|
+
elsif o.respond_to?(:date)
|
|
243
|
+
latest = o.date if o.date > latest
|
|
244
|
+
end
|
|
245
|
+
end
|
|
246
|
+
updated = REXML::Element.new("updated").add_text(latest.iso8601)
|
|
247
|
+
feed << updated
|
|
248
|
+
|
|
249
|
+
# Recommended feed elements
|
|
250
|
+
|
|
251
|
+
# link: A feed should contain a link back to the feed itself.
|
|
252
|
+
if options[:link]
|
|
253
|
+
link = REXML::Element.new("link")
|
|
254
|
+
link.add_attributes({ "rel" => "self", "href" => options[:link] })
|
|
255
|
+
feed << link
|
|
256
|
+
end
|
|
257
|
+
|
|
258
|
+
# author: Names one author of the feed.
|
|
259
|
+
if options[:author_name] # name is required for author
|
|
260
|
+
author = REXML::Element.new("author")
|
|
261
|
+
author_name = REXML::Element.new("name").add_text(options[:author_name])
|
|
262
|
+
author << author_name
|
|
263
|
+
if options[:author_email]
|
|
264
|
+
author_email = REXML::Element.new("email").add_text(options[:author_email])
|
|
265
|
+
author << author_email
|
|
266
|
+
end
|
|
267
|
+
if options[:author_link]
|
|
268
|
+
author_link = REXML::Element.new("uri").add_text(options[:author_link])
|
|
269
|
+
author << author_link
|
|
270
|
+
end
|
|
271
|
+
feed << author
|
|
272
|
+
end
|
|
273
|
+
|
|
274
|
+
# Optional feed elements
|
|
275
|
+
|
|
276
|
+
# category:
|
|
277
|
+
# contributor:
|
|
278
|
+
# generator: Identifies the software used to generate the feed.
|
|
279
|
+
generator = REXML::Element.new("generator")
|
|
280
|
+
generator.add_attributes({ "uri" => "http://www.nitroproject.org", "version" => Nitro::Version })
|
|
281
|
+
generator.add_text("Nitro")
|
|
282
|
+
feed << generator
|
|
283
|
+
# icon
|
|
284
|
+
# logo
|
|
285
|
+
# rights
|
|
286
|
+
# subtitle
|
|
287
|
+
|
|
288
|
+
# Entries
|
|
289
|
+
objects.each do |o|
|
|
290
|
+
|
|
291
|
+
# new Entry (called "item" in RSS)
|
|
292
|
+
unless o.respond_to?(:to_href) and o.respond_to?(:title)
|
|
293
|
+
next
|
|
294
|
+
end
|
|
295
|
+
entry = REXML::Element.new("entry")
|
|
296
|
+
|
|
297
|
+
# Required entry elements
|
|
298
|
+
|
|
299
|
+
# id
|
|
300
|
+
if o.respond_to?(:to_href)
|
|
301
|
+
id = REXML::Element.new("id").add_text("#{options[:base]}#{fhref o}")
|
|
302
|
+
entry << id
|
|
303
|
+
end
|
|
304
|
+
|
|
305
|
+
# title
|
|
306
|
+
if o.respond_to?(:title)
|
|
307
|
+
title = REXML::Element.new("title").add_text(o.title)
|
|
308
|
+
entry << title
|
|
309
|
+
end
|
|
310
|
+
|
|
311
|
+
# updated
|
|
312
|
+
updated = Time.at(0) # a while back
|
|
313
|
+
if o.respond_to?(:update_time)
|
|
314
|
+
updated = o.update_time
|
|
315
|
+
elsif o.respond_to?(:create_time)
|
|
316
|
+
updated = o.create_time
|
|
317
|
+
elsif o.respond_to?(:date)
|
|
318
|
+
updated = o.date
|
|
319
|
+
end
|
|
320
|
+
entry << REXML::Element.new("updated").add_text(updated.iso8601)
|
|
321
|
+
|
|
322
|
+
# Recommended entry elements
|
|
323
|
+
|
|
324
|
+
# author
|
|
325
|
+
if o.respond_to?(:author)
|
|
326
|
+
|
|
327
|
+
if o.author.kind_of?(Hash)
|
|
328
|
+
oauthor = OpenStruct.new(o.author)
|
|
329
|
+
else
|
|
330
|
+
oauthor = o.author
|
|
331
|
+
end
|
|
332
|
+
|
|
333
|
+
author = REXML::Element.new("author")
|
|
334
|
+
author_name = REXML::Element.new("name").add_text(oauthor.name)
|
|
335
|
+
author << author_name
|
|
336
|
+
if oauthor.email
|
|
337
|
+
author_email = REXML::Element.new("email").add_text(oauthor.email)
|
|
338
|
+
author << author_email
|
|
339
|
+
end
|
|
340
|
+
if oauthor.link
|
|
341
|
+
author_link = REXML::Element.new("uri").add_text(oauthor.link)
|
|
342
|
+
author << author_link
|
|
343
|
+
end
|
|
344
|
+
entry << author
|
|
345
|
+
end
|
|
346
|
+
|
|
347
|
+
# summary
|
|
348
|
+
|
|
349
|
+
if o.respond_to?(:body)
|
|
350
|
+
summary = REXML::Element.new("summary")
|
|
351
|
+
#TODO: think about whether 256 chars should be a fixed limit
|
|
352
|
+
summary.add_text(o.body.first_char(256).gsub(/<[^>]+>/, ' '))
|
|
353
|
+
entry << summary
|
|
354
|
+
end
|
|
355
|
+
|
|
356
|
+
# content
|
|
357
|
+
# may have the type text, html or xhtml
|
|
358
|
+
|
|
359
|
+
if o.respond_to?(:full_content)
|
|
360
|
+
content = REXML::Element.new("content")
|
|
361
|
+
link.add_attribute("type", "html")
|
|
362
|
+
content.add_text(o.full_content)
|
|
363
|
+
entry << content
|
|
364
|
+
end
|
|
365
|
+
|
|
366
|
+
# link: An entry must contain an alternate link if there is no content element.
|
|
367
|
+
|
|
368
|
+
if o.respond_to?(:to_href)
|
|
369
|
+
link = REXML::Element.new("link")
|
|
370
|
+
link.add_attributes({ "rel" => "alternate", "href" => "#{options[:base]}#{fhref o}" })
|
|
371
|
+
entry << link
|
|
372
|
+
end
|
|
373
|
+
|
|
374
|
+
# Optional entry elements
|
|
375
|
+
|
|
376
|
+
# category
|
|
377
|
+
# could be used for Tags maybe?
|
|
378
|
+
# contributor
|
|
379
|
+
# published
|
|
380
|
+
|
|
381
|
+
if o.respond_to?(:create_time)
|
|
382
|
+
published = REXML::Element.new("published")
|
|
383
|
+
published.add_text(o.create_time.iso8601)
|
|
384
|
+
entry << published
|
|
385
|
+
end
|
|
386
|
+
|
|
387
|
+
# source
|
|
388
|
+
# rights
|
|
389
|
+
|
|
390
|
+
# don't forget to add the entry to the feed
|
|
391
|
+
feed << entry
|
|
392
|
+
|
|
393
|
+
end if objects.size > 0 # objects/entries
|
|
394
|
+
|
|
395
|
+
atom << feed
|
|
396
|
+
|
|
397
|
+
return atom.to_s
|
|
398
|
+
end
|
|
399
|
+
alias_method :atom, :build_atom
|
|
400
|
+
|
|
401
|
+
# OPML 1.0 feed lists
|
|
402
|
+
# Fabian: eww, who invented OPML? Who needs it? Implementing
|
|
403
|
+
# it in a very rough way anyway though. Takes a Hash of
|
|
404
|
+
# Feeds and optional options.
|
|
405
|
+
|
|
406
|
+
def build_opml(feedhash, options = {})
|
|
407
|
+
|
|
408
|
+
# new XML Document for OPML
|
|
409
|
+
opml = REXML::Document.new
|
|
410
|
+
opml << REXML::XMLDecl.new("1.0", "utf-8")
|
|
411
|
+
|
|
412
|
+
# Root element <opml />
|
|
413
|
+
opml = REXML::Element.new("opml")
|
|
414
|
+
opml.add_attribute("version", "1.0")
|
|
415
|
+
|
|
416
|
+
# head
|
|
417
|
+
head = REXML::Element.new("head")
|
|
418
|
+
# title
|
|
419
|
+
if options[:title]
|
|
420
|
+
title = REXML::Element.new("title").add_text(options[:title])
|
|
421
|
+
head << title
|
|
422
|
+
end
|
|
423
|
+
# dateCreated
|
|
424
|
+
# dateModified
|
|
425
|
+
# ownerName
|
|
426
|
+
# ownerEmail
|
|
427
|
+
opml << head
|
|
428
|
+
|
|
429
|
+
# body
|
|
430
|
+
body = REXML::Element.new("body")
|
|
431
|
+
feedhash.each do |uri, type|
|
|
432
|
+
outline = REXML::Element.new("outline")
|
|
433
|
+
outline.add_attributes({ "type" => type, "xmlUrl" => uri })
|
|
434
|
+
body << outline
|
|
435
|
+
end
|
|
436
|
+
opml << body
|
|
437
|
+
|
|
438
|
+
return opml.to_s
|
|
439
|
+
end
|
|
440
|
+
alias_method :opml, :build_opml
|
|
441
|
+
|
|
442
|
+
|
|
443
|
+
# Helper
|
|
444
|
+
|
|
445
|
+
def fhref(obj)
|
|
446
|
+
"/#{obj.to_href}".squeeze('/')
|
|
447
|
+
end
|
|
448
|
+
|
|
449
|
+
end
|
|
450
|
+
|
|
451
|
+
end
|