feedtools 0.2.22 → 0.2.23

Sign up to get free protection for your applications and to get access to all the features.
@@ -0,0 +1,252 @@
1
+ #--
2
+ # Copyright (c) 2005 Robert Aman
3
+ #
4
+ # Permission is hereby granted, free of charge, to any person obtaining
5
+ # a copy of this software and associated documentation files (the
6
+ # "Software"), to deal in the Software without restriction, including
7
+ # without limitation the rights to use, copy, modify, merge, publish,
8
+ # distribute, sublicense, and/or sell copies of the Software, and to
9
+ # permit persons to whom the Software is furnished to do so, subject to
10
+ # the following conditions:
11
+ #
12
+ # The above copyright notice and this permission notice shall be
13
+ # included in all copies or substantial portions of the Software.
14
+ #
15
+ # THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
16
+ # EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
17
+ # MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
18
+ # NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
19
+ # LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
20
+ # OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
21
+ # WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
22
+ #++
23
+
24
+ module FeedTools
25
+ # Represents a feed/feed item's category
26
+ class Category
27
+
28
+ # The category term value
29
+ attr_accessor :term
30
+ # The categorization scheme
31
+ attr_accessor :scheme
32
+ # A human-readable description of the category
33
+ attr_accessor :label
34
+
35
+ alias_method :value, :term
36
+ alias_method :category, :term
37
+ alias_method :domain, :scheme
38
+ end
39
+
40
+ # Represents a feed/feed item's author
41
+ class Author
42
+
43
+ # The author's real name
44
+ attr_accessor :name
45
+ # The author's email address
46
+ attr_accessor :email
47
+ # The url of the author's homepage
48
+ attr_accessor :href
49
+ # The raw value of the author tag if present
50
+ attr_accessor :raw
51
+
52
+ alias_method :url, :href
53
+ alias_method :url=, :href=
54
+ alias_method :uri, :href
55
+ alias_method :uri=, :href=
56
+ end
57
+
58
+ # Represents a feed's image
59
+ class Image
60
+
61
+ # The image's title
62
+ attr_accessor :title
63
+ # The image's description
64
+ attr_accessor :description
65
+ # The url of the image that is being linked to
66
+ attr_accessor :href
67
+ # The url to link the image to
68
+ attr_accessor :link
69
+ # The width of the image
70
+ attr_accessor :width
71
+ # The height of the image
72
+ attr_accessor :height
73
+ # The style of the image
74
+ # Possible values are "icon", "image", or "image-wide"
75
+ attr_accessor :style
76
+
77
+ alias_method :url, :href
78
+ alias_method :url=, :href=
79
+ end
80
+
81
+ # Represents a feed's text input element.
82
+ # Be aware that this will be ignored for feed generation. It's a
83
+ # pointless element that aggregators usually ignore and it doesn't have an
84
+ # equivalent in all feeds types.
85
+ class TextInput
86
+
87
+ # The label of the Submit button in the text input area.
88
+ attr_accessor :title
89
+ # The description explains the text input area.
90
+ attr_accessor :description
91
+ # The URL of the CGI script that processes text input requests.
92
+ attr_accessor :link
93
+ # The name of the text object in the text input area.
94
+ attr_accessor :name
95
+ end
96
+
97
+ # Represents a feed's cloud.
98
+ # Be aware that this will be ignored for feed generation.
99
+ class Cloud
100
+
101
+ # The domain of the cloud.
102
+ attr_accessor :domain
103
+ # The path for the cloud.
104
+ attr_accessor :path
105
+ # The port the cloud is listening on.
106
+ attr_accessor :port
107
+ # The web services protocol the cloud uses.
108
+ # Possible values are either "xml-rpc" or "soap".
109
+ attr_accessor :protocol
110
+ # The procedure to use to request notification.
111
+ attr_accessor :register_procedure
112
+ end
113
+
114
+ # Represents a simple hyperlink
115
+ class Link
116
+ # The url that is being linked to
117
+ attr_accessor :href
118
+ # The language of the resource being linked to
119
+ attr_accessor :hreflang
120
+ # The relation type of the link
121
+ attr_accessor :rel
122
+ # The mime type of the link
123
+ attr_accessor :type
124
+ # The title of the hyperlink
125
+ attr_accessor :title
126
+ # The length of the resource being linked to in bytes
127
+ attr_accessor :length
128
+
129
+ alias_method :url, :href
130
+ alias_method :url=, :href=
131
+ end
132
+
133
+ # This class stores information about a feed item's file enclosures.
134
+ class Enclosure
135
+ # The url for the enclosure
136
+ attr_accessor :href
137
+ # The MIME type of the file referenced by the enclosure
138
+ attr_accessor :type
139
+ # The size of the file referenced by the enclosure
140
+ attr_accessor :file_size
141
+ # The total play time of the file referenced by the enclosure
142
+ attr_accessor :duration
143
+ # The height in pixels of the enclosed media
144
+ attr_accessor :height
145
+ # The width in pixels of the enclosed media
146
+ attr_accessor :width
147
+ # The bitrate of the enclosed media
148
+ attr_accessor :bitrate
149
+ # The framerate of the enclosed media
150
+ attr_accessor :framerate
151
+ # The thumbnail for this enclosure
152
+ attr_accessor :thumbnail
153
+ # The categories for this enclosure
154
+ attr_accessor :categories
155
+ # A hash of the enclosed file
156
+ attr_accessor :hash
157
+ # A website containing some kind of media player instead of a direct
158
+ # link to the media file.
159
+ attr_accessor :player
160
+ # A list of credits for the enclosed media
161
+ attr_accessor :credits
162
+ # A text rendition of the enclosed media
163
+ attr_accessor :text
164
+ # A list of alternate version of the enclosed media file
165
+ attr_accessor :versions
166
+ # The default version of the enclosed media file
167
+ attr_accessor :default_version
168
+
169
+ alias_method :url, :href
170
+ alias_method :url=, :href=
171
+ alias_method :link, :href
172
+ alias_method :link=, :href=
173
+
174
+ # Returns true if this is the default enclosure
175
+ def is_default?
176
+ return @is_default
177
+ end
178
+
179
+ # Sets whether this is the default enclosure for the media group
180
+ def is_default=(new_is_default)
181
+ @is_default = new_is_default
182
+ end
183
+
184
+ # Returns true if the enclosure contains explicit material
185
+ def explicit?
186
+ return @explicit
187
+ end
188
+
189
+ # Sets the explicit attribute on the enclosure
190
+ def explicit=(new_explicit)
191
+ @explicit = new_explicit
192
+ end
193
+
194
+ # Determines if the object is a sample, or the full version of the
195
+ # object, or if it is a stream.
196
+ # Possible values are 'sample', 'full', 'nonstop'.
197
+ def expression
198
+ return @expression
199
+ end
200
+
201
+ # Sets the expression attribute on the enclosure.
202
+ # Allowed values are 'sample', 'full', 'nonstop'.
203
+ def expression=(new_expression)
204
+ unless ['sample', 'full', 'nonstop'].include? new_expression.downcase
205
+ raise ArgumentError,
206
+ "Permitted values are 'sample', 'full', 'nonstop'."
207
+ end
208
+ @expression = new_expression.downcase
209
+ end
210
+
211
+ # Returns true if this enclosure contains audio content
212
+ def audio?
213
+ unless self.type.nil?
214
+ return true if (self.type =~ /^audio/) != nil
215
+ end
216
+ # TODO: create a more complete list
217
+ # =================================
218
+ audio_extensions = ['mp3', 'm4a', 'm4p', 'wav', 'ogg', 'wma']
219
+ audio_extensions.each do |extension|
220
+ if (url =~ /#{extension}$/) != nil
221
+ return true
222
+ end
223
+ end
224
+ return false
225
+ end
226
+
227
+ # Returns true if this enclosure contains video content
228
+ def video?
229
+ unless self.type.nil?
230
+ return true if (self.type =~ /^video/) != nil
231
+ return true if self.type == "image/mov"
232
+ end
233
+ # TODO: create a more complete list
234
+ # =================================
235
+ video_extensions = ['mov', 'mp4', 'avi', 'wmv', 'asf']
236
+ video_extensions.each do |extension|
237
+ if (url =~ /#{extension}$/) != nil
238
+ return true
239
+ end
240
+ end
241
+ return false
242
+ end
243
+ end
244
+
245
+ # TODO: Make these actual classes instead of structs
246
+ # ==================================================
247
+ EnclosureHash = Struct.new( "EnclosureHash", :hash, :type )
248
+ EnclosurePlayer = Struct.new( "EnclosurePlayer", :url, :height, :width )
249
+ EnclosureCredit = Struct.new( "EnclosureCredit", :name, :role )
250
+ EnclosureThumbnail = Struct.new( "EnclosureThumbnail", :url, :height,
251
+ :width )
252
+ end
@@ -31,8 +31,6 @@ require 'feed_tools/helpers/generic_helper'
31
31
  # the FeedTools library.
32
32
  module FeedTools
33
33
  module FeedToolsHelper
34
- include FeedTools::GenericHelper
35
- private :validate_options
36
34
 
37
35
  @@default_local_path = File.expand_path('.')
38
36
 
@@ -45,12 +43,15 @@ module FeedTools
45
43
  def self.default_local_path=(new_default_local_path)
46
44
  @@default_local_path = new_default_local_path
47
45
  end
48
-
49
- protected
46
+
47
+ protected
50
48
  # Loads a feed within a block for more consistent syntax and control
51
49
  # over the FeedTools environment.
52
50
  def with_feed(options={})
53
- validate_options([ :from_file, :from_url, :from_data, :feed_cache ],
51
+ FeedTools::GenericHelper.validate_options([ :from_file,
52
+ :from_url,
53
+ :from_data,
54
+ :feed_cache ],
54
55
  options.keys)
55
56
  options = { :feed_cache =>
56
57
  FeedTools.configurations[:feed_cache] }.merge(options)
@@ -22,179 +22,37 @@
22
22
  #++
23
23
 
24
24
  require 'feed_tools'
25
- require 'rexml/document'
26
25
 
27
26
  module FeedTools
28
27
  # Generic methods needed in numerous places throughout FeedTools
29
28
  module GenericHelper
30
29
  # Raises an exception if an invalid option has been specified to prevent
31
30
  # misspellings from slipping through
32
- def validate_options(valid_option_keys, supplied_option_keys)
31
+ def self.validate_options(valid_option_keys, supplied_option_keys)
33
32
  unknown_option_keys = supplied_option_keys - valid_option_keys
34
33
  unless unknown_option_keys.empty?
35
34
  raise "Unknown options: #{unknown_option_keys}"
36
35
  end
37
36
  end
38
37
 
39
- # Selects the first non-blank result.
40
- def select_not_blank(results, &block)
41
- for result in results
42
- blank_result = false
43
- if !block.nil?
44
- blank_result = block.call(result)
45
- else
46
- blank_result = result.to_s.blank?
47
- end
48
- unless result.nil? || blank_result
49
- return result
50
- end
51
- end
52
- return nil
53
- end
54
-
55
- # Runs through a list of XPath queries on an element or document and
56
- # returns the first non-blank result. Subsequent XPath queries will
57
- # not be evaluated.
58
- def try_xpaths(element, xpath_list,
59
- options={}, &block)
60
- validate_options([ :select_result_value ],
61
- options.keys)
62
- options = { :select_result_value => false }.merge(options)
63
-
64
- result = nil
65
- if element.nil?
38
+ # Nifty little method that takes a block and returns nil if recursion
39
+ # occurs or the block's result value if it doesn't.
40
+ def self.recursion_trap(lock_object, &block)
41
+ if @lock_ids.nil?
42
+ @lock_ids = []
43
+ end
44
+ if !@lock_ids.include?(lock_object.object_id)
45
+ @lock_ids << lock_object.object_id
46
+ else
66
47
  return nil
67
48
  end
68
- for xpath in xpath_list
69
- result = REXML::XPath.liberal_first(element, xpath,
70
- FEED_TOOLS_NAMESPACES)
71
- if options[:select_result_value] && !result.nil?
72
- if result.respond_to?(:value)
73
- result = result.value
74
- else
75
- result = result.to_s
76
- end
77
- end
78
- blank_result = false
79
- if block_given?
80
- blank_result = yield(result)
81
- else
82
- blank_result = result.to_s.blank?
83
- end
84
- if !blank_result
85
- if result.respond_to? :strip
86
- result.strip!
87
- end
88
- return result
89
- end
90
- end
91
- for xpath in xpath_list
92
- result = REXML::XPath.liberal_first(element, xpath)
93
- if options[:select_result_value] && !result.nil?
94
- if result.respond_to?(:value)
95
- result = result.value
96
- else
97
- result = result.to_s
98
- end
99
- end
100
- blank_result = false
101
- if block_given?
102
- blank_result = yield(result)
103
- else
104
- blank_result = result.to_s.blank?
105
- end
106
- if !blank_result
107
- if result.respond_to? :strip
108
- result.strip!
109
- end
110
- return result
111
- end
112
- end
113
- for xpath in xpath_list
114
- if xpath =~ /^\w+$/
115
- for child in element.children
116
- if child.class == REXML::Element
117
- if child.name.downcase == xpath.downcase
118
- result = child
119
- end
120
- end
121
- end
122
- if options[:select_result_value] && !result.nil?
123
- if result.respond_to?(:value)
124
- result = result.value
125
- else
126
- result = result.to_s
127
- end
128
- end
129
- blank_result = false
130
- if block_given?
131
- blank_result = yield(result)
132
- else
133
- blank_result = result.to_s.blank?
134
- end
135
- if !blank_result
136
- if result.respond_to? :strip
137
- result.strip!
138
- end
139
- return result
140
- end
141
- end
142
- end
143
- return nil
144
- end
145
-
146
- # Runs through a list of XPath queries on an element or document and
147
- # returns the first non-empty result. Subsequent XPath queries will
148
- # not be evaluated.
149
- def try_xpaths_all(element, xpath_list, options={})
150
- validate_options([ :select_result_value ],
151
- options.keys)
152
- options = { :select_result_value => false }.merge(options)
153
-
154
- results = []
155
- if element.nil?
156
- return []
157
- end
158
- for xpath in xpath_list
159
- results = REXML::XPath.liberal_match(element, xpath,
160
- FEED_TOOLS_NAMESPACES)
161
- if options[:select_result_value] && !results.nil? && !results.empty?
162
- results =
163
- results.map { |x| x.respond_to?(:value) ? x.value : x.to_s }
164
- end
165
- if results.blank?
166
- results = REXML::XPath.liberal_match(element, xpath)
167
- else
168
- return results
169
- end
170
- if options[:select_result_value] && !results.nil? && !results.empty?
171
- results =
172
- results.map { |x| x.respond_to?(:value) ? x.value : x.to_s }
173
- end
174
- if !results.blank?
175
- return results
176
- end
177
- end
178
- for xpath in xpath_list
179
- if xpath =~ /^\w+$/
180
- results = []
181
- for child in element.children
182
- if child.class == REXML::Element
183
- if child.name.downcase == xpath.downcase
184
- results << child
185
- end
186
- end
187
- end
188
- if options[:select_result_value] && !results.nil? && !results.empty?
189
- results =
190
- results.map { |x| x.inner_xml }
191
- end
192
- if !results.blank?
193
- return results
194
- end
195
- end
49
+ begin
50
+ result = block.call
51
+ rescue SystemStackError
52
+ result = nil
196
53
  end
197
- return []
54
+ @lock_ids.delete(lock_object.object_id)
55
+ return result
198
56
  end
199
57
  end
200
58
  end