radamanthus-superfeedr-ruby 0.4.3
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/LICENSE +20 -0
- data/README.rdoc +71 -0
- data/Rakefile +80 -0
- data/VERSION.yml +4 -0
- data/lib/config.yaml +2 -0
- data/lib/stanzas/iq_query_stanza.rb +36 -0
- data/lib/stanzas/notification_stanza.rb +278 -0
- data/lib/stanzas/subscribe_query_stanza.rb +26 -0
- data/lib/stanzas/subscriptions_query_stanza.rb +18 -0
- data/lib/stanzas/unsubscribe_query_stanza.rb +27 -0
- data/lib/superfeedr.rb +213 -0
- data/spec/spec.opts +4 -0
- data/spec/spec_helper.rb +11 -0
- data/spec/stanzas/iq_query_stanza_spec.rb +30 -0
- data/spec/stanzas/notifications_stanza_spec.rb +184 -0
- data/spec/stanzas/subscribe_stanza_spec.rb +15 -0
- data/spec/stanzas/subscriptions_stanza_spec.rb +15 -0
- data/spec/stanzas/unsubscribe_stanza_spec.rb +15 -0
- data/spec/superfeedr_ruby_spec.rb +237 -0
- metadata +115 -0
data/LICENSE
ADDED
@@ -0,0 +1,20 @@
|
|
1
|
+
Copyright (c) 2009 julien
|
2
|
+
|
3
|
+
Permission is hereby granted, free of charge, to any person obtaining
|
4
|
+
a copy of this software and associated documentation files (the
|
5
|
+
"Software"), to deal in the Software without restriction, including
|
6
|
+
without limitation the rights to use, copy, modify, merge, publish,
|
7
|
+
distribute, sublicense, and/or sell copies of the Software, and to
|
8
|
+
permit persons to whom the Software is furnished to do so, subject to
|
9
|
+
the following conditions:
|
10
|
+
|
11
|
+
The above copyright notice and this permission notice shall be
|
12
|
+
included in all copies or substantial portions of the Software.
|
13
|
+
|
14
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
15
|
+
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
16
|
+
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
|
17
|
+
NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
|
18
|
+
LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
|
19
|
+
OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
|
20
|
+
WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
data/README.rdoc
ADDED
@@ -0,0 +1,71 @@
|
|
1
|
+
= superfeedr-ruby
|
2
|
+
|
3
|
+
Yes, the gem is called superfeedr-ruby, but the library is superfeedr only ;)
|
4
|
+
|
5
|
+
{Superfeedr.com}[http://superfeedr.com] is a Real-time Cloud Feed Parsing service
|
6
|
+
|
7
|
+
* *Realtime* : By combining different technologies we can notify you of new entries in less than 15 minutes (or it's free)
|
8
|
+
* *No* *more* *polling* : Stop wasting your time and resources fetching 'old data', and stop being late when there is new data.
|
9
|
+
* *Simplicity* : give us urls, and we'll do the rest. Check our docs and tutorials.
|
10
|
+
* *Standardization* : No more nightmares with gazillions of formats, we will send you strict ATOM, whatever the original feed format is.
|
11
|
+
* *Multi*-*channel* : receive the notifications by XMPP, on a local JID or your own external JID, or using WebHooks.
|
12
|
+
* *Cost* *saving* : we will match the cost of your existing system.
|
13
|
+
|
14
|
+
== FEATURES:
|
15
|
+
|
16
|
+
* Subscribe to a feed
|
17
|
+
|
18
|
+
Superfeedr.subscribe("http://github.com/superfeedr.atom") do |result|
|
19
|
+
puts "Yay, subscribed to the github Atom feed for Superfeedr" if result
|
20
|
+
end
|
21
|
+
|
22
|
+
* Unsubscribe from a feed
|
23
|
+
|
24
|
+
Superfeedr.unsubscribe("http://github.com/superfeedr.atom") do |result|
|
25
|
+
puts "Sad, you unsubscribed from the github Atom feed for Superfeedr" if result
|
26
|
+
end
|
27
|
+
|
28
|
+
* List subscriptions by page
|
29
|
+
|
30
|
+
Superfeedr.subscriptions(5) do |page, feeds|
|
31
|
+
puts "On page #{page}" :
|
32
|
+
puts feeds.inspect
|
33
|
+
end
|
34
|
+
|
35
|
+
* Receive notifications
|
36
|
+
|
37
|
+
Superfeedr.on_notification do |notification|
|
38
|
+
puts "The feed #{notification.feed_url} has been fetched (#{notification.http_status}: #{notification.message_status}) and will be fetched again in #{(notification.next_fetch - Time.now)/60} minutes."
|
39
|
+
notification.entries.each do |e|
|
40
|
+
puts " - #{e.title} (#{e.link}) was published (#{e.published}) with #{e.unique_id} as unique id : \n #{e.summary} (#{e.chunk}/#{e.chunks})"
|
41
|
+
end
|
42
|
+
end
|
43
|
+
|
44
|
+
== Install
|
45
|
+
|
46
|
+
Install the gem from Gemcutter :
|
47
|
+
|
48
|
+
sudo gem install superfeedr-ruby
|
49
|
+
|
50
|
+
Source :
|
51
|
+
|
52
|
+
git clone git@github.com:superfeedr/superfeedr-ruby.git
|
53
|
+
|
54
|
+
|
55
|
+
== Example
|
56
|
+
|
57
|
+
Create a directory called superfeedr-example. In the superfeedr-example directory create a file called example.rb and copy the code from the gist below into that file. Replace "demo@superfeedr.com" with "your-username@superfeedr.com" and replace the "*******" with your Superfeedr password. Next, create a directory called log in the superfeedr-example directory. Finally, run it with:
|
58
|
+
|
59
|
+
ruby example.rb
|
60
|
+
|
61
|
+
Example code: {Gist}[http://gist.github.com/110247]
|
62
|
+
|
63
|
+
== REQUIREMENTS:
|
64
|
+
|
65
|
+
{Skates}[http://github.com/julien51/skates/tree/master] : please note that there are 2 gems named Baylon. The one you want is the one about XMPP, you can get it with :
|
66
|
+
|
67
|
+
sudo gem install skates
|
68
|
+
|
69
|
+
== Copyright
|
70
|
+
|
71
|
+
Copyright (c) 2009 julien. See LICENSE for details.
|
data/Rakefile
ADDED
@@ -0,0 +1,80 @@
|
|
1
|
+
require 'rubygems'
|
2
|
+
require 'rake'
|
3
|
+
|
4
|
+
begin
|
5
|
+
require 'jeweler'
|
6
|
+
Jeweler::Tasks.new do |gem|
|
7
|
+
config = YAML.load(File.read('VERSION.yml'))
|
8
|
+
version = "#{config[:major]}.#{config[:minor]}.#{config[:patch]}"
|
9
|
+
gem.name = "superfeedr-ruby"
|
10
|
+
gem.version = "#{version}"
|
11
|
+
gem.summary = %Q{Ruby Client for the Superfeedr}
|
12
|
+
gem.email = "julien.genestoux@gmail.com"
|
13
|
+
gem.homepage = "http://github.com/julien51/superfeedr-ruby"
|
14
|
+
gem.authors = ["julien Genestoux"]
|
15
|
+
gem.rubyforge_project = "superfeedr-ruby"
|
16
|
+
gem.add_dependency('skates')
|
17
|
+
gem.add_dependency('nokogiri')
|
18
|
+
gem.has_rdoc = true
|
19
|
+
gem.homepage = 'http://github.com/julien51/superfeedr-ruby/'
|
20
|
+
gem.files = FileList["[A-Z]*", "{bin,generators,lib,test,spec}/**/*"]
|
21
|
+
end
|
22
|
+
rescue LoadError
|
23
|
+
puts "Jeweler not available. Install it with: sudo gem install technicalpickles-jeweler -s http://gems.github.com"
|
24
|
+
end
|
25
|
+
|
26
|
+
require 'spec/rake/spectask'
|
27
|
+
Spec::Rake::SpecTask.new(:spec) do |spec|
|
28
|
+
spec.libs << 'lib' << 'spec'
|
29
|
+
spec.spec_files = FileList['spec/**/*_spec.rb']
|
30
|
+
end
|
31
|
+
|
32
|
+
Spec::Rake::SpecTask.new(:rcov) do |spec|
|
33
|
+
spec.libs << 'lib' << 'spec'
|
34
|
+
spec.pattern = 'spec/**/*_spec.rb'
|
35
|
+
spec.rcov = true
|
36
|
+
end
|
37
|
+
|
38
|
+
|
39
|
+
task :default => :spec
|
40
|
+
|
41
|
+
require 'rake/rdoctask'
|
42
|
+
Rake::RDocTask.new do |rdoc|
|
43
|
+
if File.exist?('VERSION.yml')
|
44
|
+
config = YAML.load(File.read('VERSION.yml'))
|
45
|
+
version = "#{config[:major]}.#{config[:minor]}.#{config[:patch]}"
|
46
|
+
else
|
47
|
+
version = ""
|
48
|
+
end
|
49
|
+
|
50
|
+
rdoc.rdoc_dir = 'rdoc'
|
51
|
+
rdoc.title = "superfeedr-ruby #{version}"
|
52
|
+
rdoc.rdoc_files.include('README*')
|
53
|
+
rdoc.rdoc_files.include('lib/**/*.rb')
|
54
|
+
end
|
55
|
+
|
56
|
+
begin
|
57
|
+
require 'rake/contrib/sshpublisher'
|
58
|
+
namespace :rubyforge do
|
59
|
+
|
60
|
+
desc "Release gem and RDoc documentation to RubyForge"
|
61
|
+
task :release => ["rubyforge:release:gem", "rubyforge:release:docs"]
|
62
|
+
|
63
|
+
namespace :release do
|
64
|
+
desc "Publish RDoc to RubyForge."
|
65
|
+
task :docs => [:rdoc] do
|
66
|
+
config = YAML.load(
|
67
|
+
File.read(File.expand_path('~/.rubyforge/user-config.yml'))
|
68
|
+
)
|
69
|
+
|
70
|
+
host = "#{config['username']}@rubyforge.org"
|
71
|
+
remote_dir = "/var/www/gforge-projects/superfeedr-ruby/"
|
72
|
+
local_dir = 'rdoc'
|
73
|
+
|
74
|
+
Rake::SshDirPublisher.new(host, remote_dir, local_dir).upload
|
75
|
+
end
|
76
|
+
end
|
77
|
+
end
|
78
|
+
rescue LoadError
|
79
|
+
puts "Rake SshDirPublisher is unavailable or your rubyforge environment is not configured."
|
80
|
+
end
|
data/VERSION.yml
ADDED
data/lib/config.yaml
ADDED
@@ -0,0 +1,36 @@
|
|
1
|
+
class IqQueryStanza
|
2
|
+
|
3
|
+
def initialize(params = {})
|
4
|
+
@doc = Nokogiri::XML::Document.new
|
5
|
+
@iq = Nokogiri::XML::Node.new("iq", @doc)
|
6
|
+
@iq["type"] = params[:type].to_s
|
7
|
+
@iq["to"] = Superfeedr.conf[:superfeedr_jid]
|
8
|
+
@iq["id"] = "#{random_iq_id}"
|
9
|
+
@iq["from"] = params[:from] if params[:from]
|
10
|
+
end
|
11
|
+
|
12
|
+
def type
|
13
|
+
@iq["type"]
|
14
|
+
end
|
15
|
+
|
16
|
+
def to
|
17
|
+
@iq["to"]
|
18
|
+
end
|
19
|
+
|
20
|
+
def from
|
21
|
+
@iq["from"]
|
22
|
+
end
|
23
|
+
|
24
|
+
def id
|
25
|
+
@iq["id"]
|
26
|
+
end
|
27
|
+
|
28
|
+
def random_iq_id
|
29
|
+
rand(1000)
|
30
|
+
end
|
31
|
+
|
32
|
+
def to_s
|
33
|
+
@iq.to_s
|
34
|
+
end
|
35
|
+
|
36
|
+
end
|
@@ -0,0 +1,278 @@
|
|
1
|
+
##
|
2
|
+
# Repesents the items published by the firehoser (the feed entries).
|
3
|
+
# They have accessors for the following fields :
|
4
|
+
# - title
|
5
|
+
# - summary
|
6
|
+
# - link
|
7
|
+
# - published
|
8
|
+
# - unique_id
|
9
|
+
# - chunks (long entries might be notified in several chunks)
|
10
|
+
# - chunk (current chunk out of chunks)
|
11
|
+
#
|
12
|
+
# <item xmlns="http://jabber.org/protocol/pubsub" chunks="2" chunk="1">
|
13
|
+
# <entry xmlns="http://www.w3.org/2005/Atom" xml:lang="">
|
14
|
+
# <title>cool</title>
|
15
|
+
# <content type="text">cool</content>
|
16
|
+
# <id>tag:pubhubsubbub-example-app,2009:ahhwdWJzdWJodWJidWItZXhhbXBsZS1hcHByDQsSBUVudHJ5GMGOEAw</id>
|
17
|
+
# <published>2010-03-25T16:57:18Z</published>
|
18
|
+
# <link type="text/html" href="http://pubsubhubbub-example-app.appspot.com/ahhwdWJzdWJodWJidWItZXhhbXBsZS1hcHByDQsSBUVudHJ5GMGOEAw" title="" rel="alternate"/>
|
19
|
+
# </entry>
|
20
|
+
# </item>
|
21
|
+
|
22
|
+
require 'time'
|
23
|
+
|
24
|
+
class Link
|
25
|
+
def initialize(node)
|
26
|
+
@node = node
|
27
|
+
end
|
28
|
+
|
29
|
+
def title
|
30
|
+
@node["title"]
|
31
|
+
end
|
32
|
+
|
33
|
+
def href
|
34
|
+
@node["href"]
|
35
|
+
end
|
36
|
+
|
37
|
+
def rel
|
38
|
+
@node["rel"]
|
39
|
+
end
|
40
|
+
|
41
|
+
def type
|
42
|
+
@node["type"]
|
43
|
+
end
|
44
|
+
|
45
|
+
end
|
46
|
+
|
47
|
+
class Author
|
48
|
+
def initialize(node)
|
49
|
+
@node = node
|
50
|
+
end
|
51
|
+
|
52
|
+
def name
|
53
|
+
if !@name
|
54
|
+
if name = @node.at_xpath("./atom:name", {"atom" => "http://www.w3.org/2005/Atom"})
|
55
|
+
@name = name.text
|
56
|
+
end
|
57
|
+
end
|
58
|
+
end
|
59
|
+
|
60
|
+
def uri
|
61
|
+
if !@uri
|
62
|
+
if uri = @node.at_xpath("./atom:uri", {"atom" => "http://www.w3.org/2005/Atom"})
|
63
|
+
@uri = uri.text
|
64
|
+
end
|
65
|
+
end
|
66
|
+
end
|
67
|
+
|
68
|
+
def email
|
69
|
+
if !@email
|
70
|
+
if email = @node.at_xpath("./atom:email", {"atom" => "http://www.w3.org/2005/Atom"})
|
71
|
+
@email = email.text
|
72
|
+
end
|
73
|
+
end
|
74
|
+
end
|
75
|
+
|
76
|
+
end
|
77
|
+
|
78
|
+
class Category
|
79
|
+
def initialize(node)
|
80
|
+
@node = node
|
81
|
+
end
|
82
|
+
|
83
|
+
def term
|
84
|
+
@node["term"]
|
85
|
+
end
|
86
|
+
end
|
87
|
+
|
88
|
+
class Location
|
89
|
+
def initialize(node)
|
90
|
+
@node = node
|
91
|
+
end
|
92
|
+
|
93
|
+
def point
|
94
|
+
@point ||= @node.text
|
95
|
+
end
|
96
|
+
|
97
|
+
def lat
|
98
|
+
@lat ||= point.split().first.to_f
|
99
|
+
end
|
100
|
+
|
101
|
+
def lon
|
102
|
+
@lon ||= point.split().last.to_f
|
103
|
+
end
|
104
|
+
|
105
|
+
end
|
106
|
+
|
107
|
+
class Item
|
108
|
+
|
109
|
+
def initialize(node)
|
110
|
+
@node = node
|
111
|
+
end
|
112
|
+
|
113
|
+
def title
|
114
|
+
@title ||= @node.at_xpath("./atom:entry/atom:title", {"atom" => "http://www.w3.org/2005/Atom"}).text
|
115
|
+
end
|
116
|
+
|
117
|
+
def summary
|
118
|
+
if !@summary
|
119
|
+
if summary = @node.at_xpath("./atom:entry/atom:summary", {"atom" => "http://www.w3.org/2005/Atom"})
|
120
|
+
@summary = summary.text
|
121
|
+
end
|
122
|
+
end
|
123
|
+
@summary
|
124
|
+
end
|
125
|
+
|
126
|
+
def content
|
127
|
+
if !@content
|
128
|
+
if content = @node.at_xpath("./atom:entry/atom:content", {"atom" => "http://www.w3.org/2005/Atom"})
|
129
|
+
@content = content.text
|
130
|
+
end
|
131
|
+
end
|
132
|
+
@content
|
133
|
+
end
|
134
|
+
|
135
|
+
def unique_id
|
136
|
+
@unique_id ||= @node.at_xpath("./atom:entry/atom:id", {"atom" => "http://www.w3.org/2005/Atom"}).text
|
137
|
+
end
|
138
|
+
|
139
|
+
def published
|
140
|
+
if !@published
|
141
|
+
if published = @node.at_xpath("./atom:entry/atom:published", {"atom" => "http://www.w3.org/2005/Atom"}).text
|
142
|
+
@published = Time.parse(published)
|
143
|
+
end
|
144
|
+
end
|
145
|
+
@published
|
146
|
+
end
|
147
|
+
|
148
|
+
def chunks
|
149
|
+
@node["chunks"].to_i
|
150
|
+
end
|
151
|
+
|
152
|
+
def chunk
|
153
|
+
@node["chunk"].to_i
|
154
|
+
end
|
155
|
+
|
156
|
+
def links
|
157
|
+
if !@links
|
158
|
+
@links = []
|
159
|
+
@node.xpath("./atom:entry/atom:link", {"atom" => "http://www.w3.org/2005/Atom"}).each do |node|
|
160
|
+
@links.push(Link.new(node))
|
161
|
+
end
|
162
|
+
end
|
163
|
+
@links
|
164
|
+
end
|
165
|
+
|
166
|
+
def authors
|
167
|
+
if !@authors
|
168
|
+
@authors = []
|
169
|
+
@node.xpath("./atom:entry/atom:author", {"atom" => "http://www.w3.org/2005/Atom"}).each do |node|
|
170
|
+
@authors.push(Author.new(node))
|
171
|
+
end
|
172
|
+
end
|
173
|
+
@authors
|
174
|
+
end
|
175
|
+
|
176
|
+
def categories
|
177
|
+
if !@categories
|
178
|
+
@categories = []
|
179
|
+
@node.xpath("./atom:entry/atom:category", {"atom" => "http://www.w3.org/2005/Atom"}).each do |node|
|
180
|
+
@categories.push(Category.new(node))
|
181
|
+
end
|
182
|
+
end
|
183
|
+
@categories
|
184
|
+
end
|
185
|
+
|
186
|
+
def locations
|
187
|
+
if !@locations
|
188
|
+
@locations = []
|
189
|
+
@node.xpath("./atom:entry/georss:point", {"atom" => "http://www.w3.org/2005/Atom", "georss" => "http://www.georss.org/georss"}).each do |node|
|
190
|
+
@locations.push(Location.new(node))
|
191
|
+
end
|
192
|
+
end
|
193
|
+
@locations
|
194
|
+
end
|
195
|
+
|
196
|
+
end
|
197
|
+
|
198
|
+
|
199
|
+
##
|
200
|
+
# Notification : sent every time a feed has been fetched. It has the following methods:
|
201
|
+
# - message_status : a simple message that gives information about the last fetch
|
202
|
+
# - http_status : status of the http response
|
203
|
+
# - feed_url : url of the feed
|
204
|
+
# - next_fetch : Time when the feed will be fetched again (this is purely informative and it might change)
|
205
|
+
# - items : array of new items detected (might be empty)
|
206
|
+
#
|
207
|
+
#
|
208
|
+
# <message xmlns="jabber:client" from="firehoser.superfeedr.com" to="julien@superfeedr.com">
|
209
|
+
# <event xmlns="http://jabber.org/protocol/pubsub#event">
|
210
|
+
# <status xmlns="http://superfeedr.com/xmpp-pubsub-ext" feed="http://pubsubhubbub-example-app.appspot.com/feed">
|
211
|
+
# <http code="200">25002 bytes fetched in 0.73s for 1 new entries.</http>
|
212
|
+
# <next_fetch>2010-03-25T17:06:30+00:00</next_fetch>
|
213
|
+
# <title>PubSubHubBub example app</title>
|
214
|
+
# </status>
|
215
|
+
# <items node="http://pubsubhubbub-example-app.appspot.com/feed">
|
216
|
+
# <item xmlns="http://jabber.org/protocol/pubsub" chunks="1" chunk="1">
|
217
|
+
# <entry xmlns="http://www.w3.org/2005/Atom" xml:lang="" xmlns:xml="http://www.w3.org/XML/1998/namespace">
|
218
|
+
# <title>cool</title>
|
219
|
+
# <content type="text">cool</content>
|
220
|
+
# <id>tag:pubhubsubbub-example-app,2009:ahhwdWJzdWJodWJidWItZXhhbXBsZS1hcHByDQsSBUVudHJ5GMGOEAw</id>
|
221
|
+
# <published>2010-03-25T16:57:18Z</published>
|
222
|
+
# <link type="text/html" href="http://pubsubhubbub-example-app.appspot.com/ahhwdWJzdWJodWJidWItZXhhbXBsZS1hcHByDQsSBUVudHJ5GMGOEAw" title="" rel="alternate"/>
|
223
|
+
# </entry>
|
224
|
+
# <entry xmlns="http://www.w3.org/2005/Atom" xml:lang="" xmlns:xml="http://www.w3.org/XML/1998/namespace">
|
225
|
+
# <title>great</title>
|
226
|
+
# <content type="text">great</content>
|
227
|
+
# <id>tag:pubhubsubbub-example-app,2009:ahhwdWJzdWJodWJidWItZXhhbXBsZS1hcHByDQsSBUVudHJ5GMGOEAx</id>
|
228
|
+
# <published>2010-03-25T16:57:19Z</published>
|
229
|
+
# <link type="text/html" href="http://pubsubhubbub-example-app.appspot.com/ahhwdWJzdWJodWJidWItZXhhbXBsZS1hcHByDQsSBUVudHJ5GMGOEAx" title="" rel="alternate"/>
|
230
|
+
# </entry>
|
231
|
+
# </item>
|
232
|
+
# </items>
|
233
|
+
# </event>
|
234
|
+
# </message>
|
235
|
+
|
236
|
+
class NotificationStanza < Skates::Base::Stanza
|
237
|
+
|
238
|
+
XMLNS = {
|
239
|
+
"ps" => "http://jabber.org/protocol/pubsub#event",
|
240
|
+
"ps2" => "http://jabber.org/protocol/pubsub",
|
241
|
+
"sf" => "http://superfeedr.com/xmpp-pubsub-ext" } unless defined? XMLNS
|
242
|
+
|
243
|
+
def next_fetch
|
244
|
+
if !@next_fetch
|
245
|
+
time = @node.at_xpath("./ps:event/sf:status/sf:next_fetch", XMLNS).text
|
246
|
+
@next_fetch = Time.parse(time)
|
247
|
+
end
|
248
|
+
@next_fetch
|
249
|
+
end
|
250
|
+
|
251
|
+
def http_status
|
252
|
+
@http_status ||= @node.at_xpath("./ps:event/sf:status/sf:http/@code", XMLNS).text.to_i
|
253
|
+
end
|
254
|
+
|
255
|
+
def feed_url
|
256
|
+
@feed_url ||= @node.at_xpath("./ps:event/sf:status/@feed", XMLNS).text
|
257
|
+
end
|
258
|
+
|
259
|
+
def message_status
|
260
|
+
@message_status ||= @node.at_xpath("./ps:event/sf:status/sf:http", XMLNS).text
|
261
|
+
end
|
262
|
+
|
263
|
+
def title
|
264
|
+
@title ||= @node.at_xpath("./ps:event/sf:status/sf:title", XMLNS).text
|
265
|
+
end
|
266
|
+
|
267
|
+
def entries
|
268
|
+
if !@entries
|
269
|
+
@entries = []
|
270
|
+
@node.xpath("./ps:event/ps:items/ps2:item", XMLNS).each do |node|
|
271
|
+
@entries.push(Item.new(node))
|
272
|
+
end
|
273
|
+
end
|
274
|
+
@entries
|
275
|
+
end
|
276
|
+
|
277
|
+
end
|
278
|
+
|