This repository has been archived by the owner on Jan 2, 2018. It is now read-only.
/
rss_parser.rb
161 lines (141 loc) · 4.6 KB
/
rss_parser.rb
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
framework 'Cocoa'
# Asynchronous and non blocking RSS Parser based on NSXMLParser.
# The parser will progressively parse a feed and yield each item to the provided block.
#
# The parser can also trigger delegate methods, you can define the following delegate method on the receiving object:
# def when_parser_initializes; end
# def when_parser_parses; end
# def when_parser_is_done; end
# def when_parser_errors; end
#
# @see https://developer.apple.com/library/ios/#documentation/Cocoa/Reference/Foundation/Classes/NSXMLParser_Class/Reference/Reference.html
#
# @usage example:
# feed = RSSParser.new(URL)
# feed.delegate = self
# feed.parse do |item|
# print item.link
# end
# def when_parser_is_done
# App.alert('parsing complete')
# end
#
module BubbleWrap
class RSSParser
attr_accessor :parser, :source, :doc, :debug, :delegate, :parser_error
attr_reader :state
# RSSItem is a simple class that holds all of RSS items.
# Extend this class to display/process the item differently.
class RSSItem
attr_accessor :title, :description, :link, :guid, :pubDate, :enclosure
def initialize
@title, @description, @link, @pubDate, @guid = '', '', '', '', ''
end
def to_hash
{
:title => title,
:description => description,
:link => link,
:pubDate => pubDate,
:guid => guid,
:enclosure => enclosure
}
end
end
def initialize(input, data=false)
if data
data_to_parse = input.respond_to?(:to_data) ? input.to_data : input
@source = data_to_parse
else
url = input.is_a?(NSURL) ? input : NSURL.alloc.initWithString(input)
@source = url
end
self.state = :initializes
self
end
def state=(new_state)
@state = new_state
callback_meth = "when_parser_#{new_state}"
if self.delegate && self.delegate.respond_to?(callback_meth)
self.delegate.send(callback_meth)
end
end
# Starts the parsing and send each parsed item through its block.
#
# Usage:
# feed.parse do |item|
# puts item.link
# end
def parse(&block)
@block = block
fetch_source_data do |data|
@parser = NSXMLParser.alloc.initWithData(data)
@parser.shouldProcessNamespaces = true
@parser.delegate ||= self
@parser.parse
end
end
# Delegate getting called when parsing starts
def parserDidStartDocument(parser)
puts "starting parsing.." if debug
self.state = :parses
end
# Delegate being called when an element starts being processed
def parser(parser, didStartElement:element, namespaceURI:uri, qualifiedName:name, attributes:attrs)
if element == 'item'
@current_item = RSSItem.new
elsif element == 'enclosure'
@current_item.enclosure = attrs
end
@current_element = element
end
# as the parser finds characters, this method is being called
def parser(parser, foundCharacters:string)
if @current_element && @current_item && @current_item.respond_to?(@current_element)
el = @current_item.send(@current_element)
el << string if el.respond_to?(:<<)
end
end
# method called when an element is done being parsed
def parser(parser, didEndElement:element, namespaceURI:uri, qualifiedName:name)
if element == 'item'
@block.call(@current_item) if @block
else
@current_element = nil
end
end
# method called when the parser encounters an error
# error can be retrieved with parserError
def parser(parser, parseErrorOccurred:parse_error)
puts "parseErrorOccurred" if debug
@parser_error = parse_error
self.state = :errors
end
# delegate getting called when the parsing is done
# If a block was set, it will be called on each parsed items
def parserDidEndDocument(parser)
puts "done parsing" if debug
self.state = :is_done
end
def parserError
@parser_error || @parser.parserError
end
# TODO: implement
# parser:validationErrorOccurred:
# parser:foundCDATA:
protected
def fetch_source_data(&blk)
if @source.is_a?(NSURL)
HTTP.get(@source.absoluteString) do |response|
if response.ok?
blk.call(response.body)
else
parser(parser, parseErrorOccurred:"HTTP request failed (#{response})")
end
end
else
yield @source
end
end
end
end