howcast 0.7.4 → 0.7.15

Sign up to get free protection for your applications and to get access to all the features.
Files changed (47) hide show
  1. data/.gitignore +6 -0
  2. data/.rspec +2 -0
  3. data/.rvmrc +1 -0
  4. data/Gemfile +2 -0
  5. data/Gemfile.lock +38 -0
  6. data/{README.markdown → README.md} +44 -13
  7. data/Rakefile +6 -39
  8. data/VERSION +1 -1
  9. data/fixtures/categories.xml +2230 -0
  10. data/fixtures/category.1585.xml +24 -0
  11. data/fixtures/homepage.staff_videos.xml +315 -0
  12. data/fixtures/invalid.api_key.xml +6 -0
  13. data/fixtures/playlist.4566.xml +484 -0
  14. data/fixtures/users.someone.profile.videos.xml +440 -0
  15. data/fixtures/video.233.generated.xml +1563 -0
  16. data/fixtures/video.233.xml +830 -0
  17. data/howcast.gemspec +31 -90
  18. data/lib/howcast.rb +1 -1
  19. data/lib/howcast/client.rb +1 -1
  20. data/lib/howcast/client/base.rb +232 -188
  21. data/lib/howcast/client/category.rb +31 -13
  22. data/lib/howcast/client/homepage.rb +10 -8
  23. data/lib/howcast/client/marker.rb +2 -0
  24. data/lib/howcast/client/playlist.rb +2 -0
  25. data/lib/howcast/client/search.rb +11 -11
  26. data/lib/howcast/client/type.rb +48 -0
  27. data/lib/howcast/client/user.rb +2 -0
  28. data/lib/howcast/client/utils.rb +53 -0
  29. data/lib/howcast/client/video.rb +39 -25
  30. data/lib/howcast/ext/string.rb +8 -0
  31. data/lib/howcast/hpricot/elements.rb +22 -0
  32. data/lib/howcast/version.rb +3 -0
  33. data/script/github-test.rb +24 -0
  34. data/spec/howcast/client/base_spec.rb +2 -2
  35. data/spec/howcast/client/category_spec.rb +41 -2
  36. data/spec/howcast/client/homepage_spec.rb +8 -8
  37. data/spec/howcast/client/playlist_spec.rb +6 -4
  38. data/spec/howcast/client/search_spec.rb +7 -8
  39. data/spec/howcast/client/user_spec.rb +9 -7
  40. data/spec/howcast/client/video_spec.rb +106 -22
  41. data/spec/spec_helper.rb +4 -7
  42. data/spec/xml_fixtures_helper.rb +20 -2895
  43. metadata +94 -28
  44. data/CHANGELOG +0 -108
  45. data/Manifest +0 -19
  46. data/howcast-0.7.3.gem +0 -0
  47. data/spec/string_matchers_helper.rb +0 -22
@@ -1,97 +1,38 @@
1
- # Generated by jeweler
2
- # DO NOT EDIT THIS FILE DIRECTLY
3
- # Instead, edit Jeweler::Tasks in Rakefile, and run the gemspec command
4
1
  # -*- encoding: utf-8 -*-
2
+ $:.push File.expand_path("../lib", __FILE__)
3
+ require "howcast/version"
5
4
 
6
5
  Gem::Specification.new do |s|
7
- s.name = %q{howcast}
8
- s.version = "0.7.4"
9
-
10
- s.required_rubygems_version = Gem::Requirement.new(">= 0") if s.respond_to? :required_rubygems_version=
11
- s.authors = ["Jingshen Jimmy Zhang", "Ian Smith-Heisters"]
12
- s.date = %q{2010-04-05}
6
+ s.name = "howcast"
7
+ s.version = Howcast::VERSION
8
+ s.platform = Gem::Platform::RUBY
9
+ s.authors = ["Jingshen Jimmy Zhang", "Ian Smith-Heisters", "Juris Galang"]
10
+ s.email = ["support@howcast.com"]
11
+ s.homepage = "http://api.howcast.com/"
12
+ s.summary = %q{Ruby-bindings for the Howcast API}
13
13
  s.description = %q{ Howcast offers an Application Programming Interface (API) which allows
14
- developers to build applications that interface with Howcast. The Howcast
15
- API is RESTful (REpresentational State Transfer) and users of this API will
16
- be able: 1) Retreive detailed information about a single video, including
17
- metadata such as title, description, video views, rating etc; 2) Retrieve a
18
- list of videos restricted by a set of filters offered by Howcast and sorted
19
- using several metrics that you can specify (most recent, most views, etc);
20
- 3) Search for video; 4) And much more. Note: Before you can use our APIs,
21
- you must register an API key, that is submitted with each request.
22
- }
23
- s.email = %q{support@howcast.com}
24
- s.extra_rdoc_files = [
25
- "README.markdown"
26
- ]
27
- s.files = [
28
- "CHANGELOG",
29
- "License.txt",
30
- "Manifest",
31
- "README.markdown",
32
- "Rakefile",
33
- "VERSION",
34
- "howcast-0.7.3.gem",
35
- "howcast.gemspec",
36
- "lib/howcast.rb",
37
- "lib/howcast/client.rb",
38
- "lib/howcast/client/base.rb",
39
- "lib/howcast/client/category.rb",
40
- "lib/howcast/client/homepage.rb",
41
- "lib/howcast/client/marker.rb",
42
- "lib/howcast/client/playlist.rb",
43
- "lib/howcast/client/search.rb",
44
- "lib/howcast/client/user.rb",
45
- "lib/howcast/client/video.rb",
46
- "lib/howcast/errors.rb",
47
- "lib/howcast/logging.rb",
48
- "spec/howcast/client/base_spec.rb",
49
- "spec/howcast/client/category_spec.rb",
50
- "spec/howcast/client/homepage_spec.rb",
51
- "spec/howcast/client/playlist_spec.rb",
52
- "spec/howcast/client/search_spec.rb",
53
- "spec/howcast/client/user_spec.rb",
54
- "spec/howcast/client/video_spec.rb",
55
- "spec/output_capture_helper.rb",
56
- "spec/spec.opts",
57
- "spec/spec_helper.rb",
58
- "spec/string_matchers_helper.rb",
59
- "spec/xml_fixtures_helper.rb",
60
- "tasks/github.rake"
61
- ]
62
- s.homepage = %q{http://github.com/howcast/howcast-gem}
63
- s.rdoc_options = ["--charset=UTF-8"]
64
- s.require_paths = ["lib"]
65
- s.rubygems_version = %q{1.3.6}
66
- s.summary = %q{Howcast API Ruby Wrapper}
67
- s.test_files = [
68
- "spec/howcast/client/base_spec.rb",
69
- "spec/howcast/client/category_spec.rb",
70
- "spec/howcast/client/homepage_spec.rb",
71
- "spec/howcast/client/playlist_spec.rb",
72
- "spec/howcast/client/search_spec.rb",
73
- "spec/howcast/client/user_spec.rb",
74
- "spec/howcast/client/video_spec.rb",
75
- "spec/output_capture_helper.rb",
76
- "spec/spec_helper.rb",
77
- "spec/string_matchers_helper.rb",
78
- "spec/xml_fixtures_helper.rb"
79
- ]
14
+ developers to build applications that interface with Howcast. The Howcast
15
+ API is RESTful (REpresentational State Transfer) and users of this API will
16
+ be able: 1) Retreive detailed information about a single video, including
17
+ metadata such as title, description, video views, rating etc; 2) Retrieve a
18
+ list of videos restricted by a set of filters offered by Howcast and sorted
19
+ using several metrics that you can specify (most recent, most views, etc);
20
+ 3) Search for video; 4) And much more. Note: Before you can use our APIs,
21
+ you must register an API key, that is submitted with each request.
22
+ }
80
23
 
81
- if s.respond_to? :specification_version then
82
- current_version = Gem::Specification::CURRENT_SPECIFICATION_VERSION
83
- s.specification_version = 3
24
+ s.rubyforge_project = "howcast"
84
25
 
85
- if Gem::Version.new(Gem::RubyGemsVersion) >= Gem::Version.new('1.2.0') then
86
- s.add_runtime_dependency(%q<hpricot>, [">= 0"])
87
- s.add_development_dependency(%q<rspec>, [">= 0"])
88
- else
89
- s.add_dependency(%q<hpricot>, [">= 0"])
90
- s.add_dependency(%q<rspec>, [">= 0"])
91
- end
92
- else
93
- s.add_dependency(%q<hpricot>, [">= 0"])
94
- s.add_dependency(%q<rspec>, [">= 0"])
95
- end
26
+ s.files = `git ls-files`.split("\n")
27
+ s.test_files = `git ls-files -- {test,spec,features}/*`.split("\n")
28
+ s.executables = `git ls-files -- bin/*`.split("\n").map{ |f| File.basename(f) }
29
+ s.require_paths = ["lib"]
30
+
31
+ s.add_runtime_dependency(%q<hpricot>, [">= 0"])
32
+ s.add_runtime_dependency(%q<nokogiri>, [">= 0"])
33
+
34
+ #s.add_development_dependency(%q<jeweler>, [">= 0"])
35
+ s.add_development_dependency(%q<rake>, [">= 0"])
36
+ s.add_development_dependency(%q<rspec>, [">= 0"])
37
+ s.add_development_dependency(%q<ruby-debug>, [">= 0"])
96
38
  end
97
-
@@ -1,6 +1,6 @@
1
1
  module Howcast; end
2
2
 
3
- %w(howcast/client howcast/errors howcast/logging).each do |dependency|
3
+ %w(howcast/version howcast/client howcast/errors howcast/logging).each do |dependency|
4
4
  require(File.expand_path(File.join(File.dirname(__FILE__), dependency)))
5
5
  end
6
6
 
@@ -2,6 +2,6 @@
2
2
  class Howcast::Client
3
3
  end
4
4
 
5
- %w(client/base client/video client/search client/category client/marker client/user client/homepage client/playlist).each do |dependency|
5
+ %w(client/utils client/base client/video client/search client/category client/marker client/user client/homepage client/playlist client/type).each do |dependency|
6
6
  require(File.expand_path(File.join(File.dirname(__FILE__), dependency)))
7
7
  end
@@ -22,10 +22,16 @@
22
22
  #++
23
23
 
24
24
  require 'rubygems'
25
+ require 'benchmark'
26
+ require 'timeout'
25
27
  require 'hpricot'
28
+ require 'nokogiri'
26
29
  require 'open-uri'
27
30
  require 'uri'
28
31
 
32
+ require File.expand_path(File.join(File.dirname(__FILE__), '../hpricot/elements'))
33
+ require File.expand_path(File.join(File.dirname(__FILE__), '../ext/string'))
34
+
29
35
  class Howcast::Client
30
36
  attr_accessor :key
31
37
 
@@ -56,210 +62,248 @@ class Howcast::Client
56
62
  end
57
63
 
58
64
  def base_uri
59
- @base_uri ||= URI.parse("http://www.howcast.com")
65
+ @base_uri ||= URI.parse("http://api.howcast.com")
60
66
  end
61
67
  end
68
+
62
69
  protected
63
- # Establishes a connection with the Howcast API. Will auto append the api key, that was used
64
- # to instantiate the Client object, to the URL
65
- #
66
- # === Inputs
67
- #
68
- # * <tt>relative_path</tt> -- should be the path after <tt>http://www.howcast.com/</tt>
69
- #
70
- # === Outputs
71
- #
72
- # Hpricot object encapsulating the xml returned from the Howcast API
73
- #
74
- # === Exceptions
75
- #
76
- # * <tt>Howcast::ApiNotFound</tt> -- raised if the requested +relative_path+ is malformed or not available (404)
77
- # * <tt>Howcast::ApiKeyNotFound</tt> -- raised if the +api_key+ is invalid
78
- #
79
- # === Examples
80
- #
81
- # Get the Hpricot data for most recent howcast studios videos
82
- # establish_connection("videos/most_recent/howcast_studios.xml")
83
- def establish_connection(relative_path_and_query)
84
- uri = self.class.base_uri.dup
85
- relative_path_and_query = '/' + relative_path_and_query unless relative_path_and_query[0] == '/'
86
- uri.path, uri.query = *relative_path_and_query.split('?')
87
- h = Hpricot.XML(open(url = attach_api_key(uri)))
88
- Howcast.log.info "Established connection with: '#{url}'"
89
- raise Howcast::ApiKeyNotFound if h.at(:error) && h.at(:error).inner_html.match(/Invalid API Key/)
90
- return h
70
+ # Establishes a connection with the Howcast API. Will auto append the api key, that was used
71
+ # to instantiate the Client object, to the URL
72
+ #
73
+ # === Inputs
74
+ #
75
+ # * <tt>relative_path</tt> -- should be the path after <tt>http://www.howcast.com/</tt>
76
+ #
77
+ # === Outputs
78
+ #
79
+ # Hpricot object encapsulating the xml returned from the Howcast API
80
+ #
81
+ # === Exceptions
82
+ #
83
+ # * <tt>Howcast::ApiNotFound</tt> -- raised if the requested +relative_path+ is malformed or not available (404)
84
+ # * <tt>Howcast::ApiKeyNotFound</tt> -- raised if the +api_key+ is invalid
85
+ #
86
+ # === Examples
87
+ #
88
+ # Get the Hpricot data for most recent howcast studios videos
89
+ # establish_connection("videos/most_recent/howcast_studios.xml")
90
+ def establish_connection(relative_path_and_query)
91
+ begin
92
+ uri = self.class.base_uri.dup
93
+ relative_path_and_query = '/' + relative_path_and_query unless relative_path_and_query[0] == '/'
94
+ uri.path, uri.query = *relative_path_and_query.split('?')
95
+
96
+ doc = nil
97
+ time = Benchmark.realtime do
98
+ doc = Hpricot.XML open(attach_api_key uri)
99
+ end
100
+ Howcast.log.info "Established connection with: '#{uri.to_s}' after #{time * 1000}ms"
101
+
102
+ raise Howcast::ApiKeyNotFound \
103
+ if doc.at(:err) && doc.at(:err)['msg'].match(/Invalid API Key/)
104
+
105
+ doc
106
+ rescue Timeout::Error
107
+ raise Howcast::ApiError, "Timed-out after #{time * 1000}ms while attempting to access the API at #{uri.to_s}"
91
108
  rescue URI::InvalidURIError
92
- raise Howcast::ApiNotFound.new("Invalid URL #{url.inspect} requested. Refer to the Howcast API for supported URL's")
93
- rescue OpenURI::HTTPError => boom
94
- raise Howcast::ApiError.new("HTTP error #{boom.message} accessing the API. Refer to the Howcast API for supported URL's")
109
+ raise Howcast::ApiNotFound, "Invalid URL #{uri.to_s} requested."
110
+ rescue OpenURI::HTTPError => e
111
+ raise Howcast::ApiError, "HTTP error #{e.message} accessing the API at #{uri.to_s}."
95
112
  end
96
-
97
- # Parses the xml for a single item from +xml+ and creates a new +klass+ object
98
- #
99
- # === Inputs
100
- #
101
- # * <tt>xml</tt> -- See below for a sample xml input
102
- # * <tt>klass</tt> -- Class to create - Video | Category supported
103
- #
104
- # Sample input xml
105
- # <video>
106
- # <id>1086</id>
107
- # <rating>96</rating>
108
- # <title>How To Choose a Paintbrush</title>
109
- # <category-id>19</category-id>
110
- # <description>
111
- # <![CDATA[Yes, you could just use your fingers, but selecting the best brushes for your painting might achieve a slightly more grown-up result.]]>
112
- # </description>
113
- # <views>362</views>
114
- # <permalink>http://www.howcast.com/guides/1086-How-To-Choose-a-Paintbrush</permalink>
115
- # <username>michaelrsanchez</username>
116
- # <created-at>Thu, 20 Dec 2007 14:14:58 -0800</created-at>
117
- # </video>
118
- #
119
- # === Outputs
120
- #
121
- # +klass+ object with initialized attributes
122
- def parse_single_xml(xml, klass)
123
- hash = {}
124
- klass.attr_accessors.each do |attribute|
125
- node_name = attribute.to_s.gsub("_", "-") # xml schema uses hyphens for spaces, but ruby uses underscores
126
- if node_name == "category-hierarchy"
127
- hash[attribute] = category_hierarchy_for(xml) unless xml.at(node_name).nil?
128
- elsif node_name == "ingredients"
129
- hash[attribute] = ingredients_for(xml) unless xml.at(node_name).nil?
130
- elsif node_name == "markers"
131
- hash[attribute] = markers_for(xml) unless xml.at(node_name).nil?
132
- elsif node_name == "related-videos"
133
- hash[attribute] = related_videos_for(xml) unless xml.at(node_name).nil?
134
- elsif node_name == "videos"
135
- hash[attribute] = videos_for(xml) unless xml.at(node_name).nil?
136
- elsif node_name == "playlist-thumbnail-url"
137
- # TO DO: Resolve this hack, xml attributes aren't named consistently
138
- hash[attribute] = !xml.at(node_name).nil? ? xml.at(node_name).inner_text.strip : xml.at("thumbnail-url").inner_text.strip
139
- else
140
- hash[attribute] = !xml.at(node_name).nil? ? xml.at(node_name).inner_text.strip : ""
141
- end
113
+ end
114
+
115
+ # Parses the xml for a single item from +xml+ and creates a new +klass+ object
116
+ #
117
+ # === Inputs
118
+ #
119
+ # * <tt>xml</tt> -- See below for a sample xml input
120
+ # * <tt>klass</tt> -- Class to create - Video | Category supported
121
+ #
122
+ # Sample input xml
123
+ # <video>
124
+ # <id>1086</id>
125
+ # <rating>96</rating>
126
+ # <title>How To Choose a Paintbrush</title>
127
+ # <category-id>19</category-id>
128
+ # <description>
129
+ # <![CDATA[Yes, you could just use your fingers, but selecting the best brushes for your painting might achieve a slightly more grown-up result.]]>
130
+ # </description>
131
+ # <views>362</views>
132
+ # <permalink>http://www.howcast.com/guides/1086-How-To-Choose-a-Paintbrush</permalink>
133
+ # <username>michaelrsanchez</username>
134
+ # <created-at>Thu, 20 Dec 2007 14:14:58 -0800</created-at>
135
+ # </video>
136
+ #
137
+ # === Outputs
138
+ #
139
+ # +klass+ object with initialized attributes
140
+ def parse_single_xml(xml, klass)
141
+ hash = { }
142
+ klass.attr_accessors.each do |attribute|
143
+ node_name = attribute.to_s.gsub("_", "-") # xml schema uses hyphens for spaces, but ruby uses underscores
144
+ if node_name == "category-hierarchy"
145
+ hash[attribute] = category_hierarchy_for(xml) unless xml.at(node_name).nil?
146
+ elsif node_name == "playlist-memberships"
147
+ hash[attribute] = playlist_memberships_for(xml) unless xml.at(node_name).nil?
148
+ elsif node_name == "ingredients"
149
+ hash[attribute] = ingredients_for(xml) unless xml.at(node_name).nil?
150
+ elsif node_name == "markers"
151
+ hash[attribute] = markers_for(xml) unless xml.at(node_name).nil?
152
+ elsif node_name == "related-videos"
153
+ hash[attribute] = related_videos_for(xml) unless xml.at(node_name).nil?
154
+ elsif node_name == "videos"
155
+ hash[attribute] = videos_for(xml) unless xml.at(node_name).nil?
156
+ elsif node_name == "playlist-thumbnail-url"
157
+ # TODO: Resolve this hack, xml attributes aren't named consistently
158
+ hash[attribute] = !xml.at(node_name).nil? ? xml.at(node_name).inner_text.strip : xml.at("thumbnail-url").inner_text.strip
159
+ elsif node_name == "type"
160
+ hash[attribute] = type_for(xml) unless xml.at(node_name).nil?
161
+ elsif %w{ ads-allowed mature-content }.include? node_name
162
+ hash[attribute] = !xml.at(node_name).nil? ? "true" : ""
163
+ else
164
+ hash[attribute] = !xml.at(node_name).nil? ? xml.at(node_name).inner_text.strip : ""
142
165
  end
143
- hash.values.all?{|v| v==""} ? nil : klass.new(hash)
144
- end
145
-
146
- # Creates parameters to append to a uri
147
- #
148
- # === Inputs
149
- #
150
- # Options are:
151
- # * <tt>:page</tt> -- the page number
152
- # * <tt>:use_ampersand</tt> -- boolean to return ampersand (defaults to false)
153
- #
154
- # === Outputs
155
- #
156
- # A valid suffix string to append to the end of a url
157
- #
158
- # === Examples
159
- #
160
- # "?page=2"
161
- # uri_suffix(:page => 2)
162
- # "&page=2"
163
- # uri_suffix(:page => 3, :use_ampersand => true)
164
- def uri_suffix(opts)
165
- opts && opts[:page] ? "#{opts[:use_ampersand] ? '&' : '?'}page=#{opts[:page]}" : ""
166
166
  end
167
+ hash.values.all?{ |v| (v == "") or (v.respond_to?(:empty?) and v.empty?) } ? nil : klass.new(hash)
168
+ end
167
169
 
168
- # Appends the api key to a uri and returns the appended uri
169
- #
170
- # === Inputs
171
- #
172
- # * <tt>uri</tt> -- the URI object to append the api_key to
173
- #
174
- # === Outputs
175
- #
176
- # The uri with the api_key appended to the query string
177
- #
178
- # === Examples
179
- #
180
- # attach_api_key(URI.parse("http://www.howcast.com")).to_s
181
- # => "http://www.howcast.com?api_key=APIKEYHERE"
182
- #
183
- # attach_api_key(URI.parse("http://www.howcast.com/videos/most_recent/all?page=2")).to_s
184
- # => "http://www.howcast.com/videos/most_recent/all?page=2&api_key=APIKEYHERE"
185
- def attach_api_key(uri)
186
- uri = uri.dup
187
- key = "api_key=#{self.key}"
188
- uri.query = uri.query.to_s.strip != "" ? uri.query+"&"+key : key
189
- uri
190
- end
170
+ # Creates parameters to append to a uri
171
+ #
172
+ # === Inputs
173
+ #
174
+ # Options are:
175
+ # * <tt>:page</tt> -- the page number
176
+ # * <tt>:use_ampersand</tt> -- boolean to return ampersand (defaults to false)
177
+ #
178
+ # === Outputs
179
+ #
180
+ # A valid suffix string to append to the end of a url
181
+ #
182
+ # === Examples
183
+ #
184
+ # "?page=2"
185
+ # uri_suffix(:page => 2)
186
+ # "&page=2"
187
+ # uri_suffix(:page => 3, :use_ampersand => true)
188
+ def uri_suffix(opts)
189
+ opts && opts[:page] ? "#{opts[:use_ampersand] ? '&' : '?'}page=#{opts[:page]}" : ""
190
+ end
191
+
192
+ # Appends the api key to a uri and returns the appended uri
193
+ #
194
+ # === Inputs
195
+ #
196
+ # * <tt>uri</tt> -- the URI object to append the api_key to
197
+ #
198
+ # === Outputs
199
+ #
200
+ # The uri with the api_key appended to the query string
201
+ #
202
+ # === Examples
203
+ #
204
+ # attach_api_key(URI.parse("http://www.howcast.com")).to_s
205
+ # => "http://www.howcast.com?api_key=APIKEYHERE"
206
+ #
207
+ # attach_api_key(URI.parse("http://www.howcast.com/videos/most_recent/all?page=2")).to_s
208
+ # => "http://www.howcast.com/videos/most_recent/all?page=2&api_key=APIKEYHERE"
209
+ def attach_api_key(uri)
210
+ uri = uri.dup
211
+ key = "api_key=#{self.key}"
212
+ uri.query = uri.query.to_s.strip != "" ? uri.query+"&"+key : key
213
+ uri
214
+ end
191
215
 
192
- # From merb/core_ext/hash.rb, line 87
193
- def hash_to_params hash
194
- params = ''
195
- stack = []
216
+ # From merb/core_ext/hash.rb, line 87
217
+ def hash_to_params hash
218
+ params = ''
219
+ stack = []
196
220
 
197
- hash.each do |k, v|
198
- if v.is_a?(Hash)
199
- stack << [k,v]
200
- else
201
- params << "#{k}=#{v}&"
202
- end
221
+ hash.keys.sort{ |a, b| a.to_s <=> b.to_s }.each do |k|
222
+ v = hash[k]
223
+ if v.is_a?(Hash)
224
+ stack << [k,v]
225
+ else
226
+ params << "#{k}=#{v}&"
203
227
  end
228
+ end
204
229
 
205
- stack.each do |parent, h|
206
- h.each do |k, v|
207
- if v.is_a?(Hash)
208
- stack << ["#{parent}[#{k}]", v]
209
- else
210
- params << "#{parent}[#{k}]=#{v}&"
211
- end
230
+ stack.each do |parent, h|
231
+ h.each do |k, v|
232
+ if v.is_a?(Hash)
233
+ stack << ["#{parent}[#{k}]", v]
234
+ else
235
+ params << "#{parent}[#{k}]=#{v}&"
212
236
  end
213
237
  end
214
-
215
- params.chop! # trailing &
216
- params
217
238
  end
239
+
240
+ params.chop! # trailing &
241
+ params
242
+ end
218
243
 
219
244
  private
220
- def category_hierarchy_for(xml)
221
- categories = []
222
- node = xml.at('category-hierarchy')
223
- node.children_of_type('category').each do |child|
224
- category = Category.new(:id => child['id'], :parent_id => child['parent_id'], :name => child.inner_text)
225
- categories << category
226
- end unless node.nil?
227
- categories
228
- end
229
-
230
- def ingredients_for(xml)
231
- ingredients = []
232
- node = xml.at('ingredients')
233
- node.children_of_type('ingredient').each do |child|
234
- ingredients << child.inner_text.strip
235
- end unless node.nil?
236
- ingredients
237
- end
238
-
239
- def markers_for(xml)
240
- markers = []
241
- node = xml.at('markers')
242
- node.children_of_type('marker').each do |child|
243
- markers << parse_single_xml(child, Marker)
244
- end unless node.nil?
245
- markers
246
- end
247
-
248
- def related_videos_for(xml)
249
- related = []
250
- node = xml.at('related-videos')
251
- node.children_of_type('video').each do |child|
252
- related << parse_single_xml(child, Video)
253
- end unless node.nil?
254
- related
255
- end
256
-
257
- def videos_for(xml)
258
- videos = []
259
- node = xml.at('videos')
260
- node.children_of_type('video').each do |child|
261
- videos << parse_single_xml(child, Video)
262
- end unless node.nil?
263
- videos
245
+ def category_hierarchy_for(xml)
246
+ categories = []
247
+ node = xml.at('category-hierarchy')
248
+ node.children_of_type('category').each do |child|
249
+ category = Category.new(:id => child['id'], :parent_id => child['parent_id'], :name => child.inner_text, :permalink => child['permalink'])
250
+ categories << category
251
+ end unless node.nil?
252
+ categories
253
+ end
254
+
255
+ def playlist_memberships_for(xml)
256
+ memberships = []
257
+ node = xml.at('playlist-memberships')
258
+ node.children_of_type('playlist').each do |child|
259
+ playlist = Playlist.new(:id => child['id'], :title => child.inner_text)
260
+ memberships << playlist
261
+ end unless node.nil?
262
+ memberships
263
+ end
264
+
265
+ def ingredients_for(xml)
266
+ ingredients = []
267
+ node = xml.at('ingredients')
268
+ node.children_of_type('ingredient').each do |child|
269
+ ingredients << child.inner_text.strip
270
+ end unless node.nil?
271
+ ingredients
272
+ end
273
+
274
+ def markers_for(xml)
275
+ markers = []
276
+ node = xml.at('markers')
277
+ node.children_of_type('marker').each do |child|
278
+ markers << parse_single_xml(child, Marker)
279
+ end unless node.nil?
280
+ markers
281
+ end
282
+
283
+ def type_for(xml)
284
+ node = xml.at('type')
285
+ if node['status'] && node['class']
286
+ Type.new(:name => node.inner_text.strip, :kind => node['class'], :status => node['status'])
287
+ else
288
+ node.inner_text.strip
264
289
  end
290
+ end
291
+
292
+ def related_videos_for(xml)
293
+ related = []
294
+ node = xml.at('related-videos')
295
+ node.children_of_type('video').each do |child|
296
+ related << parse_single_xml(child, Video)
297
+ end unless node.nil?
298
+ related
299
+ end
300
+
301
+ def videos_for(xml)
302
+ videos = []
303
+ node = xml.at('videos')
304
+ node.children_of_type('video').each do |child|
305
+ videos << parse_single_xml(child, Video)
306
+ end unless node.nil?
307
+ videos
308
+ end
265
309
  end